Creating a WordPress Plugin to Export MySQL data as CSV

Series:
Posted on

Recently at work, a client requested the ability to export user browsing information as a Comma-separated values (CSV) file. I was asked if I could make this happen, and I was pretty sure I could figure it out. It took a couple of hours of research, but It was a fun and educational experience that lead me to new understandings of PHP, WordPress, and MySQL.

I decided to share what I learned with as much concise detail as possible and with the same process I took, because without the extremely helpful guides from the programming communities online, I would not be as strong a programmer as I am today. So thank you everyone, and read ahead for the guide.

Summary

This guide will help you write a WordPress plugin that has an Admin Page which is accessible from the Tools menu on the WordPress Dashboard. It will use PHP to retrieve data from a MySQL database, format it as a CSV file, and send it to the browser to be downloaded without storing a file on the server.

The Steps and The Skills

Step 1 – Send a CSV file to the Browser: PHP
Step 2 – Build the Database Query: MySQL, WordPress Database
Step 3 – Access the WordPress Database: WordPress Development, PHP
Step 4 – Make it a Plugin: WordPress Development, PHP, minimal HTML
Step 5 – Activate and Use the Plugin: End results
Update 1 – Add Content-Length Header: PHP

Step 1 – Send a CSV file to the Browser

The first hurdle I had to jump was figuring out how to create a CSV file in PHP and send it to the browser without storing it on the server. A few short google searches later and I came accross fputcsv() and “php://output“. The fputcsv() function accepts two parameters, a file pointer and an array, and writes the contents of the array into the file. “php://output” isn’t a function, but can be passed into the fopen() function in order to open the PHP output buffer as a file, which is how I avoided saving CSV files to the server before sending them to the browser. So I wrote a (not unit) test:

csv-test.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
/*
 * Make sure to use some namespacing for your functions:
 * Mine for this example: "fz_csv_"
 */

function fz_csv_export() {
    // Used for mock times
    $date = new DateTime();
    $ts = $date->format( 'Y-m-d H:i:s' );

    // Array of mock db data
    $result = array();

    // First record
    $result[] = array(
        "first_name" => "Faison", "last_name" => "Zutavern",
        "product_viewed" => "Sunglasses", "time_viewed" => $ts
    );

    // Second record
    $result[] = array(
        "first_name" => "Faison", "last_name" => "Zutavern",
        "product_viewed" => "Razor", "time_viewed" => $ts
    );

    // A name with a time stamp, to avoid duplicate filenames
    $filename = "report-$ts.csv";

    // Tells the browser to expect a CSV file and bring up the
    // save dialog in the browser
    header( 'Content-Type: text/csv' );
    header( 'Content-Disposition: attachment;filename='.$filename);

    // This opens up the output buffer as a "file"
    $fp = fopen('php://output', 'w');

    // Get the first record
    $hrow = $result[0];

    // Extracts the keys of the first record and writes them
    // to the output buffer in CSV format
    fputcsv($fp, array_keys($hrow));

    // Then, write every record to the output buffer in CSV format
    foreach ($result as $data) {
        fputcsv($fp, $data);
    }

    // Close the output buffer (Like you would a file)
    fclose($fp);
}

// Execute the function
fz_csv_export();
?>
  • Lines 5 – 19: On these lines, I’m building the data that we’ll pretend we received from a database query (Make sure that when you build proofs of concepts, that you make everything as close to the real deal as possible). The information that we’ll be grabbing from the database includes “first_name”, “last_name”, “product_viewed”, and “time_viewed”.
  • Lines 20 – 25: On these lines, we create a filename and append a timestamp (this is to reduce, if not eliminate, the possibility of duplicate filenames). Then, through http headers, we tell the browser that we are sending a CSV file that should be downloaded with the filename provided on line 25.
  • Line 27: This was new for me. When you pass “php://output” into the fopen() function, you get the PHP output buffer wrapped up as a file pointer. In this state, you can perform file writes as usual, but instead of being stored in a file, the file writes are sent to the browser. This is nice, because I don’t want to store unnecessary files on the site.
  • Lines 28 – 32: These lines write a CSV header to the CSV file being sent to the browser. First we put the first record in a temporary variable, then we use two functions at once: fputcsv() and array_keys(). array_keys() gives us an array containing the keys of the array passed in as an argument. Finally, fputcsv() is the function that does our heavy lifting. Once again, it accepts a file pointer and an array, as arguments respectively, and writes the contents of the array into the file as data formatted for CSV.
  • Lines 33 – 36: Here we loop through the array of data and write it to the output buffer with fputcsv().
  • Line 38: Always close what you open! In some languages, not doing this can cause spooky behavior. In PHP, it frees up memory, so do it!
  • Lines 4 & 41: Since we’re putting this into a WordPress plugin, we want this code to be easy to use. So we encapsulate the code in a function and call it at the end of the PHP file. To keep things simple, I called my function “export”, but quickly remembered that it’s possible to break other people’s plugins (OPP) if your functions are named the same as their’s. So I added a prefix for my function: “fz_csv_” (my initials and CSV). Try to make your prefixes make sense to your code.

Finally, I put the PHP file on my local server, pointed my browser to the PHP file, and my browser told me to download a CSV file. After downloading, I opened the CSV file with LibreOffice Calc (aka, free Microsoft Excel) and had proof of success:

Mock CSV Report Data opened in LibreOffice Calc

Step 2 – Build the Database Query

I have a database table, called wp_fz_csv_tracking_info, that stores data tracking what product a user viewed and the time they viewed it.

Tracking Info Table Structure

The SQL query for getting that information is simple enough:

1
SELECT product_viewed, time_viewed FROM wp_fz_csv_tracking_info

When it comes to storing data in the database, WordPress does some interesting things. For the CSV I’m exporting, I needed to get the user’s first name and last name, but it’s not in the users table.

WordPress User Table's Structure

I forget how I found this, but the first and last name is stored in the WordPress usermeta table.

WordPress Usermeta Data

Look at how the first name is stored: user_id = 1, meta_key = first_name, and meta_value = Faison. Instead of having a dedicated place in the WordPress database for the first name of a user, WordPress stores the data as a chunck of data associated with both a user_id and a key that could be anything. If we wanted, we could store something like user_id = 1, meta_key = watch_preference, and meta_value = pocket watch; the possibilities are intriguingly endless.

Back on topic though, my first thought was that a simple “LEFT JOIN” wouldn’t do it.

1
2
SELECT * FROM wp_fz_csv_tracking_info
LEFT JOIN wp_usermeta ON wp_fz_csv_tracking_info.user_id = wp_usermeta.user_id

If we ran that, we would get multiple records returned for each user. I don’t know about you, but I’d rather have one record with all the information I need. To do that, I had to come up with some scary looking subquery with aliasing. Here’s how I got the first name:

1
2
3
4
SELECT first_name, product_viewed, time_viewed FROM wp_fz_csv_tracking_info
LEFT JOIN (SELECT user_id, meta_value AS first_name
FROM wp_usermeta WHERE meta_key='first_name')a
ON wp_fz_csv_tracking_info.user_id = a.user_id

In the subquery (the part between the parenthesis), we’re basically creating a quick one use table, with only user ids and first names, and naming the table “a”. By doing this, we can LEFT JOIN the subquery to the actual table and use a normal SELECT statement. We can do this for the rest of the information stored in the usermeta table, just be sure to use different alias names for each subquery.

1
2
3
4
5
6
7
8
SELECT first_name, last_name, product_viewed, time_viewed
FROM wp_fz_csv_tracking_info
LEFT JOIN (SELECT user_id, meta_value AS first_name
FROM wp_usermeta WHERE meta_key='first_name')a
ON wp_fz_csv_tracking_info.user_id = a.user_id
LEFT JOIN (SELECT user_id, meta_value AS last_name
FROM wp_usermeta WHERE meta_key='last_name')b
ON wp_fz_csv_tracking_info.user_id = b.user_id

Which Results in the following:

The SQL Results

Step 3 – Access the WordPress Database

I could have used the mysql functions, I could have used the PHP Data Object, but WordPress already has a global database object that knows the database credentials and location. With some Googling, I found this post in the WordPress support forums which says we just need to include the wp-load.php file. So I renamed my PHP file and updated the code as follows:

export.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
// This include gives us all the WordPress functionality
include_once($_SERVER['DOCUMENT_ROOT'].'/wp-load.php' );

/*
 * Make sure to use some namespacing for your functions:
 * Mine for this example: "fz_csv_"
 */

function fz_csv_export() {
    // This line gets the WordPress Database Object
    global $wpdb;

    // Here's the query, split up for easy reading
    $qry = array();
    $qry[] = "SELECT first_name, last_name, product_viewed, time_viewed";
    $qry[] = "FROM wp_fz_csv_tracking_info LEFT JOIN";
    $qry[] = "(SELECT user_id, meta_value AS first_name FROM wp_usermeta";
    $qry[] = "WHERE meta_key='first_name')a ON";
    $qry[] = "wp_fz_csv_tracking_info.user_id = a.user_id LEFT JOIN";
    $qry[] = "(SELECT user_id, meta_value AS last_name FROM wp_usermeta";
    $qry[] = "WHERE meta_key='last_name')b ON";
    $qry[] = "wp_fz_csv_tracking_info.user_id = b.user_id";

    /*
     * Use the WordPress database object to run the query and get
     * the results as an associative array
     */

    $result = $wpdb->get_results(implode(" ", $qry), ARRAY_A);

    // Check if any records were returned from the database
    if ($wpdb->num_rows > 0) {
        // Make a DateTime object and get a time stamp for the filename
        $date = new DateTime();
        $ts = $date->format("Y-m-d-G-i-s");

        // A name with a time stamp, to avoid duplicate filenames
        $filename = "report-$ts.csv";

        // Tells the browser to expect a CSV file and bring up the
        // save dialog in the browser
        header( 'Content-Type: text/csv' );
        header( 'Content-Disposition: attachment;filename='.$filename);

        // This opens up the output buffer as a "file"
        $fp = fopen('php://output', 'w');

        // Get the first record
        $hrow = $result[0];

        // Extracts the keys of the first record and writes them
        // to the output buffer in CSV format
        fputcsv($fp, array_keys($hrow));

        // Then, write every record to the output buffer in CSV format
        foreach ($result as $data) {
            fputcsv($fp, $data);
        }
        // Close the output buffer (Like you would a file)
        fclose($fp);
    }
}

// This function removes all content from the output buffer
ob_end_clean();
// Execute the function
fz_csv_export();
?>
  • Line 3: This line gives you all the functionality of WordPress. Side Note – If you have your WordPress installed in a subdirectory of your server’s document root, make sure to put the directory name before “/wp-load.php”. So on my computer, it reads “/wp/wp-load.php”
  • Line 8: This is how you get WordPress’s global database object
  • Lines 9 – 18: In these lines, I write the query as multiple Strings in an array. By doing this, I can do what I did in…
  • Line 21: Here, we use the database object’s get_results() function. The first required parameter is a string, so I use the implode() function on the array with the query and specify a space as the “glue”. By doing that, the implode() function returns one string with all parts of the array. The next parameter is a constant that tells the function to return the data as an associative array.
  • Line 23: This line is to make sure the database query returned data. If it didn’t and we let the code in the following block run, bad stuff would probably happen.
  • Line 49: This discards anything currently in the PHP output buffer. I had to add this line because the WordPress installation I was working with added several lines to the output buffer, causing my CSV file to have 3 blank lines before the CSV file’s header line.

Once again, I put export.php into my local server, direct my browser to the PHP file, and my browser has me download a CSV file. From the browser’s point of view, nothing new happened, but back on the server, our PHP script is getting access to the WordPress database and grabbing data to be returned as a CSV file.

The SQL Results

The same SQL Results as before, so we did it right.

Step 4 – Make it a Plugin

There are many great guides for making plugins for WordPress, but I learned from using this net tuts guide along with the Writing a Plugin page on the WordPress Codex. I’ll sum up what I did here, but if you need more details feel free to ask me or check out those two links.

First, I created a plugin folder in the “/wp-content/plugins” directory. I called mine “/fz_csv_exporter” which will also be the same name as my plugin. If you read guides for writing WordPress plugins, you’ll find that you can put your plugin directly in the “/wp-content/plugins” directory and everything will work well. This plugin is going to have more than one file, so I put it in a subdirectory of “/wp-content/plugins”.

Then, we create the main plugin PHP file. This file goes into the directory we just made and gets called the same thing. So in “/wp-content/plugins/fz_csv_exporter” we now have a file called “fz_csv_exporter.php”. Here are the contents of that file:

fz_csv_exporter.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
/*
 * Plugin Name: CSV Exporter
 * Description: This allows admins to export tracking info as CSV
 * Author: Faison Zutavern
 * Version: 1.0
 */


/*
 * This function encapsulates creating the admin page
 * for this plugin
 */

function fz_csv_exporter_page() {
    // This function creates a link for our admin page
    // in the Tools menu on the WordPress dashboard
    add_management_page(
        'CSV Exporter', 'Export Tracking Info',
        'export', 'fz_csv_exporter', 'fz_csv_admin'
    );
}

// The proper time to add Admin Menu Pages, is when
// the 'admin_menu' action takes place
add_action('admin_menu', 'fz_csv_exporter_page');

// This is called when a user goes to the admin page
// for this plugin
function fz_csv_admin() {
    // This line gets the admin page from another file
    // in this directory
    require( dirname( __FILE__ ) . '/admin_page.php' );
}
?>
  • Lines 2 – 7: This is the standard plugin information header.
  • Line 10: We provide this function for WordPress so that when the action specified on line 20 occurs, WordPress will call this function.
  • Line 11 – 17: The add_management_page() function allows us to add a link to our plugin page under the Tools menu. The first parameter is the Page Title, the second is the text displayed in the tools menu, the third is the capability required to access this page (‘export’ is exclusive to administrator role), the fourth is the menu slug and needs to be unique to the Tools menu, and the fifth is the name of the function that is called to display this page.
  • Line 20: This line uses the add_action() function in order to have WordPress call the function at line 8 when WordPress is building the admin panel.
  • Lines 23 – 27: This function is called when a user goes to the page specified in lines 13 – 16. Instead of putting the HTML and PHP right in this function, I use the require() function to get the admin_page from in the same directory as the plugin file.

Now we need the admin_page.php from the code above. This is also in the directory “/wp-content/plugins/fz_csv_exporter”.

admin_page.php

1
2
3
4
5
6
7
<div class="wrap">
    <h2>Export Tracking Information</h2>
    <form method="post" name="fz_csv_exporter_form" action="<?php echo plugin_dir_url( __FILE__ ); ?>export.php">
        <h3>Press the button below to export all tracking information.</h3>
        <p class="submit"><input type="submit" name="Submit" value="Export Tracking Info" /></p>
    </form>
</div>
  • Line 1: I nest my entire admin page in a div with the class wrap, because I lose some default styling of elements if I don’t.
  • Lines 3 – 4: This is the probably the most interesting part of this file. First, I use a form, because I like the default styling. Then, I specify the action to point to the same directory these files are in, using the plugin_dir_url() function so that I get a url rather than a directory path. Then, I specified the filename “export.php”

Then, I moved the export.php file, from step 3, into the plugin directory. Afterwards, I had a directory containing three files: “fz_csv_exporter.php”, “admin_page.php”, and “export.php”.

Step 5 – Activate and Use the Plugin

I assume if you made it this far, you probably know how to activate a plugin. If you don’t, WordPress has instructions for Manually installing plugins which goes over activating plugins.

After it’s activated, you should see the link to the plugin’s admin page in the Tools menu.

The Plugin Working

And here’s the admin page:

The Admin Page

And when I press the button, I’m prompted to download a CSV file which contain all the data returned from the database query from step 2.

Conclusion

That concludes the process I took in order to create a WordPress plugin for exporting information from the database to a CSV file downloaded through the browser. If something didn’t work right for you, please post a comment and I’ll do my best to help you out.

Also I’m a lifelong learner and am still getting familiar with WordPress development, so I invite constructive criticism and suggestions on everything I write. Since this is my first guide, I would definitely appreciate feedback on everything.

Thanks much everyone!

Update 1 – Add Content-Length Header

As suggested by Joel Clermont in the comments, I looked into adding a Content-Length header to the script. Joel explained that if the file is very large, “the user’s browser would be able to show a proper progress bar.”

So I decided to introduce a loop in csv-test.php from step 1, that would execute lines 10 – 19 100,000 times. When I pointed my browser to it, I ended up downloading an 8.9MB file without ever knowing what percent was complete.

Download without Content-Length, it only shows how many KB downloaded

I never sent a Content-Length header in PHP before, so I ran to google and found a forum thread on devshed’s PHP Development section. After reading that a bit, I added this to export.php:

export.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
// This includes gives us all the WordPress functionality
include_once($_SERVER['DOCUMENT_ROOT'].'/wp/wp-load.php' );

// Make sure to use some namespacing for your functions:
// Mine for this example: "fz_csv_"
function fz_csv_export() {
    // This line gets the WordPress Database Object
    global $wpdb;

    // Here's the query, split up for easy reading
    $qry = array();
    $qry[] = "SELECT first_name, last_name, product_viewed, time_viewed";
    $qry[] = "FROM wp_fz_csv_tracking_info LEFT JOIN";
    $qry[] = "(SELECT user_id, meta_value AS first_name FROM wp_usermeta";
    $qry[] = "WHERE meta_key='first_name')a ON";
    $qry[] = "wp_fz_csv_tracking_info.user_id = a.user_id LEFT JOIN";
    $qry[] = "(SELECT user_id, meta_value AS last_name FROM wp_usermeta";
    $qry[] = "WHERE meta_key='last_name')b ON";
    $qry[] = "wp_fz_csv_tracking_info.user_id = b.user_id";

    // Use the WordPress database object to run the query and get
    // the results as an associative array
    $result = $wpdb->get_results(implode(" ", $qry), ARRAY_A);

    // Check if any records were returned from the database
    if ($wpdb->num_rows > 0) {
        // Make a DateTime object and get a time stamp for the filename
        $date = new DateTime();
        $ts = $date->format("Y-m-d-G-i-s");

        // A name with a time stamp, to avoid duplicate filenames
        $filename = "report-$ts.csv";

        // Tells the browser to expect a csv file and bring up the
        // save dialog in the browser
        header( 'Content-Type: text/csv' );
        header( 'Content-Disposition: attachment;filename='.$filename);

        // This opens up the output buffer as a "file"
        $fp = fopen('php://output', 'w');

        // Get the first record
        $hrow = $result[0];

        // Extracts the keys of the first record and writes them
        // to the output buffer in csv format
        fputcsv($fp, array_keys($hrow));

        // Then, write every record to the output buffer in csv format
        foreach ($result as $data) {
            fputcsv($fp, $data);
        }

        // Close the output buffer (Like you would a file)
        fclose($fp);
       
        // Send the size of the output buffer to the browser
        $contLength = ob_get_length();
        header( 'Content-Length: '.$contLength);
    }
}
// This function removes all content from the output buffer
ob_end_clean();
// Execute the function
fz_csv_export();
?>
  • Line 46 – 48: First I use the ob_get_length() function to get the length of the content in PHP’s output buffer, then I store it in a variable to make things look nice. Finally, through the http header “Content-Length”, I send the previously mentioned length to the user’s browser.

Since my database didn’t have many records in it, I put those same lines (46 – 48) into the modified csv-test.php from step 1. When I pointed my browser to the file this time, my browser displayed a proper progress “bar” as it downloaded.

Download with Content-Length

Thanks again to Joel, and everyone else who comments and reads!

Tags: , , , , ,


  • these are available

    these are available:

    FAISONZUTAVERN.COM
    FAISONZUTAVERN.ME
    FAISONZ.COM
    FAISONZ.ME
    FZUTAVERN.COM
    FZUTAVERN.ME
    ZUTAVERN.ME

    Reply

    • faison Post author

      Thanks Scott,

      I went with FaisonZ.com, and not too painful a process either 🙂

      Reply

      • Dustin Filippini

        I’d get FaizonZutavern.com too when you have an extra $10 just to claim your name online. I dind’t want to go with my full name because Filippini is too hard to spell so I just forward that domain to dustyf.com. Now I need to make a true site/blog instead of the splash page with links to Twitter/Facebook/LinkedIn.

        Reply

  • Joel Clermont

    Faison,

    Great article and I’m glad to see you’re using the fputscsv function instead of trying to write your own csv parser. I see that done wrong all the time 🙂

    One small suggestion: After your finished writing the file to the output buffer, you could send one more header for “Content-Length” with the file size in bytes. This way, if it is a very large file, the user’s browser would be able to show a proper progress bar.

    Reply

    • faison Post author

      Thanks Joel!

      I’ve read in many places that using PHP’s built-in functions tend to work faster than writing it from scratch, since those built-in functions are actually written and compiled in C.

      Als, thanks for the suggestion, that would be really nice to have. I’ll look into that durring the weekend 🙂

      Reply

  • Mr C.

    Stumbled here from PHPMaster’s 5 Inspiring (and Useful) PHP Snippets and have to say good effort, its always good to see contributions to the WordPress community where people go to great depth in their tutorials. Coincidentally I am writing a custom solution for importing and processing CSV’s as opposed to exporting. Keep it up!

    Reply

  • Waqas

    Is there by any chance do you have still this code as plugin. If yes then can you please share its code either on email directly or just integrate it on the page. So that one can test it directly without going into the hassle of writing all the code. Thanks

    Reply

  • Pepesun

    Great tutorial, thanks:)
    Otherwise, it’s not working for me 🙁 When I call export.php, I get warnings:
    Warning: Cannot modify header information – headers already sent by (output started at /wp-content/plugins/cedh_exporter/export.php:1) in /wp-content/plugins/cedh_exporter/export.php on line 22

    Warning: Cannot modify header information – headers already sent by (output started at /wp-content/plugins/cedh_exporter/export.php:1) in /wp-content/plugins/cedh_exporter/export.php on line 23

    Line 22 and 23:
    header( ‘Content-Type: text/csv’ );
    header( ‘Content-Disposition: attachment;filename=’.$filename);

    Any idea?

    Reply

  • Jan

    My query won’t run if i do it like this.

    $qry = array();
    $qry[] = “SELECT events_date as ‘Date’, events_time as ‘Start Time’, events_opponent as ‘Opponent’, event_level as ‘Level’, events_place as ‘Location'”;
    $qry[] = “FROM wp_events”;
    $qry[] = “WHERE events_id = ‘”.$sched_id.”‘”;

    $result = $wpdb->get_results(implode(” “, $qry), ARRAY_A);

    The last query part(Where Clause) is having a problem. It won’t ready the query that way. How could i be able to fix that?

    Reply

  • Ayhan Yanbul

    Hi, THank you for your great tutorial.
    I have a problem about special characters, “şğüşöçiı”
    Is there any way about that encoding problem?

    Thank you

    Reply

  • segmant

    Hi

    This no longer exports in wordpress 3.7.1

    Reply

    • Faison Post author

      Hi Segmant,

      There was an issue in my post where it was converting -> to >. Could you go back through my sample code again and tell me if it works now?

      Thanks,
      Faison

      Reply

      • ddbs

        Hi there,

        I’ve modified the query (one line) to my own database and got the following error:

        Fatal error: Call to a member function get_results() on a non-object in C:\wamp\www\dhdev1\wp-content\plugins\aaa-my-csv-export\export.php on line 17

        Line 17: $result = $wpdb->get_results(implode(” “, $qry), ARRAY_A);

        Reply

  • Andy Perkins

    Hey I know it’s been some time since you posted this, but just wanted to say thank you. I was able to get it working as described and can now export data from a multi-table complex query based on your approach to the joins! Thanks for the help.
    Andy

    Reply

  • Stas46

    Hi,
    when i submit the form nothing happens) even no headers sends. what could be wrong??

    Reply

  • Kevin

    I was looking to do something exactly like this and it took me forever to find a solution. Your post was exactly what I needed it. Thank you so much!

    Reply

  • Maureen

    Hey Faison thank you so much, i have a doubt, I do understand the entire code, but i get lost when you say

    “Finally, I put the PHP file on my local server, pointed my browser to the PHP file, and my browser told me to download a CSV file. After downloading, I opened the CSV file with LibreOffice Calc (aka, free Microsoft Excel) and had proof of success”

    so I made a static file and put in the same directory as the functions.php where I entered the function, I called it and all I get is a printed data on browser and nothing on file, I’m lost, please explain this, thank you so much

    Reply

  • Fredrik Moström

    Hi Faison

    Will try this for a prodject I am working on. Great tutorial, nice written, easy to understand.

    Fredrik

    Reply

  • khan

    great tutorial. thanks.
    but i have an issue if you can solve it.
    when i write my file then it writes the same record multiple times on the csv file. i have follow the same idea that you share.
    here is the file link. http://pastebin.com/PXPy63Jx

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *