Export Any CSV Data from WordPress & WooCommerce

This tutorial shows you how to run custom CSV exports from WordPress & WooCommerce, so you can extract any data you want. We’ll set it up with a couple of simple exports, and it’s easy to add as many new exports as you want.

Each CSV Export will be a small PHP file that runs a WordPress query/loop, so there’s a little bit of coding here, but not much.

We’ll tie it together with a simple widget, so you can run your exports straight from the admin dashboard with a single click.

importantMake sure you’re using a custom child theme so you can edit your functions.php file.

C|ustom CSV Exports amin widget
Custom Admin Widget for CSV Exports

Build a Simple Admin Widget

The code to create a WordPress admin widget is pretty easy, so let’s jump straight in. In your custom child theme’s folder, create a file called “custom-csv-exports.php” and also make a folder called “custom-csv-exports”. Open custom-csv-exports.php and paste the following into it.

<?php

/**
 * Headwall WP Tutorials : Export CSV Data from WordPress and WooCommerce.
 *
 * https://wp-tutorials.tech/add-functionality/export-any-csv-data-from-wordpress-woocommerce/
 */

// Don't access this file directly.
defined('WPINC') || die();

/**
 * Configure our admin dashboard widget.
 */
function custom_wp_dashboard_setup() {
    wp_add_dashboard_widget(
        'custom_csv_exports', // Internal name of our widget.
        'Custom CSV Exports', // Title of our widget.
        'custom_csv_exports_widget' // The function that outputs our widget's HTML.
    );
}
add_action('wp_dashboard_setup', 'custom_wp_dashboard_setup');

/**
 * Output the HTML for the admin dashboard widget.
 */
function custom_csv_exports_widget() {
    $base_uri = get_stylesheet_directory_uri();

    // Report One - Post Properties
    printf(
        '<a href="%s" class="button widefat">Export Post Properties</a>',
        esc_url($base_uri . '/custom-csv-exports/report-one.php')
    );

    // More buttons, text, and other goodies for your
    // admin widget can go here...
    // ...
}

Now open your custom child theme’s main functions.php file and paste the following into it.

// Custom CSV Exports
require_once dirname(__FILE__) . '/custom-csv-exports.php';

Save your changes and then go to the admin area of your site. You should see your shiny new admin widget. That was easy 😎

If you look in custom_csv_exports_widget() you’ll see that all we’ve done is output an <a> element with the href set to “/custom-csv-exports/report-one.php” inside our child theme. So… what we need to do next is create report-one.php.

The Report Logic – The PHP Bit

The PHP files that drive our exports will use a straightforward logic flow, like this:

  • The file (PHP) is being called directly, so we need to call wp-load.php to instantiate WordPress.
  • Abort if the user is not logged-in as an administrator.
  • Output some HTTP headers to tell the browser to expect a CSV file (instead of an HTML document).
  • Print a comma-separated list of column names.
  • Create an array of arguments that define our query.
  • Create an array of WordPress post objects (or WooCommerce order objects).
  • For each object (row)…
    • Print a comma-separated list of values.
  • exit()

Export your Posts as CSV Data

In your custom child theme, go into the custom-csv-exports folder and create a new file called report-one.php. Then paste the following into it:

<?php

/**
 * Load WordPress. This should work in most cases, but you might need to
 * adjust it if your WordPress installation is unusual.
 */
require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/wp-load.php';

// Fail if not logged-in as administrator.
if (!current_user_can('administrator')) {
    // 'Not Authorised';
    nocache_headers();
    wp_safe_redirect(site_url());
    exit();
} else {
    $download_file_name = 'report-one.csv';

    // Tell the web browser to expect a CSV file download instead
    // of an HTML document.
    header("Content-Type: application/force-download");
    header("Content-Type: application/octet-stream");
    header("Content-Type: application/download");
    header('Content-Type: text/x-csv');
    header('Content-Disposition: attachment;filename=' . $download_file_name);

    // This lets us use the fputcsv() function.
    $output = fopen('php://output', 'w');

    $field_names = array(
        'post_id',
        'post_status',
        'post_title',
        'post_author_id',
        'post_author_name',
        'post_author_email',
    );

    $row_values = array();

    // Print the headers.
    fputcsv($output, $field_names);

    // Extract the WP_Post objects
    $query_args = array(
        'post_type' => 'post',
        'post_status' => 'publish', // Only published posts.
        'numberposts' => -1, // get all posts, not just the most recent 10.
    );
    $posts = get_posts($query_args);

    // Loop through the $posts array and print each CSV row.
    foreach ($posts as $post) {
        // Each $post is a WP_Post object.
        // https://developer.wordpress.org/reference/classes/wp_post/

        $post_id = $post->ID;
        $author_id = get_post_field('post_author', $post->ID);
        $author = get_user_by('ID', $author_id);

        // Reset all the values for this row.
        foreach ($field_names as $field_name) {
            $row_values[$field_name] = '';
        }

        // Set all the values for this row.
        $row_values['post_id'] = $post->ID;
        $row_values['post_status'] = get_post_status($post->ID);
        $row_values['post_title'] = get_the_title($post);
        $row_values['post_author_id'] = $author_id;

        if (!empty($author)) {
            $row_values['post_author_name'] = $author->display_name;
            $row_values['post_author_email'] = $author->user_email;
        }

        // Print this CSV row.
        fputcsv($output, $row_values);
    }

}

You can now export all your posts in a CSV file. Not bad!

If you want to add more fields, just add them into the $field_names array (keep the field names unique) and then set $row_values['my_new_field'] = "..." in the main loop.

Export WooCommerce Orders as CSV Data

The code to export WooCommerce orders is very similar to what we’ve just written. Let’s create Report Two. Open custom-csv-exports.php and add the following into custom_csv_exports_widget().

// Report Two  - Export orders.
printf(
    '<a href="%s" class="button widefat">Export All Orders</a>',
    esc_url($base_uri . '/custom-csv-exports/report-two.php')
);

Now go into custom-csv-exports, create a file called report-two.php and paste this lot into it.

<?php

require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/wp-load.php';

if (!current_user_can('administrator')) {
    // 'Not Authorised';
    nocache_headers();
    wp_safe_redirect(site_url());
    exit();
} else {
    $download_file_name = 'report-two.csv';

    header("Content-Type: application/force-download");
    header("Content-Type: application/octet-stream");
    header("Content-Type: application/download");
    header('Content-Type: text/x-csv');
    header('Content-Disposition: attachment;filename=' . $download_file_name);

    $output = fopen('php://output', 'w');

    $field_names = array(
        'order_id',
        'shipping_country',
        'currency',
        'order_total',
        'customer_id',
        'customer_name',
        'customer_email',
    );

    $row_values = array();

    fputcsv($output, $field_names);

    // First date is one month ago. Info for creating date intervals is here:
    // https://www.php.net/manual/en/dateinterval.construct.php
    $first_date = new DateTime('now', wp_timezone());
    $first_date->sub(new DateInterval('P1D'));
    $first_date_string = $first_date->format('Y-m-d');

    // Last date is today.
    $last_date = new DateTime('now', wp_timezone());
    $last_date_string = $last_date->format('Y-m-d');

    // wc_get_orders() is really useful..
    // Lots of good examples for $query_args on Stack Overflow.
    $query_args = array(
        'limit' => -1, // Get all orders, not just the first 10.
        'type' => 'shop_order',
        'status' => array('wc-completed', 'wc-processing'),
        'date_created' => $first_date_string . '...' . $last_date_string,
        'orderby' => 'date_created',
        'order' => 'ASC',
    );
    $orders = wc_get_orders($query_args);

    foreach ($orders as $order) {
        // Each $order is a WC_Order object.
        // https://woocommerce.github.io/code-reference/classes/WC-Order.html

        $order_id = $order->get_id();
        $customer_id = $order->get_user_id();
        $customer = get_user_by('ID', $customer_id);

        // Reset all the values for this row.
        foreach ($field_names as $field_name) {
            $row_values[$field_name] = '';
        }

        // Set all the values for this row.
        $row_values['order_id'] = $order_id;
        $row_values['shipping_country'] = $order->get_shipping_country();
        $row_values['currency'] = $order->get_currency();
        $row_values['order_total'] = $order->get_total();

        $row_values['customer_id'] = $customer_id;
        if (!empty($customer)) {
            $row_values['customer_name'] = $customer->display_name;
            $row_values['customer_email'] = $customer->user_email;
        }

        fputcsv($output, $row_values);
    }

}

The structure is very similar to report-one. The main difference is we’re trying to extract an array of WC_Order objects using wc_get_orders(), instead of an array of WP_Post objects using get_posts().

You can now run the CSV export by clicking on Export All Orders in the dashboard widget.

Keep On Going…

Keeping each report in its own PHP file is a nice way of enabling you to put custom logic in there. If you try to do an export using a single MySQL SELECT/JOIN database query, you’ll quite quickly find it difficult to do quirky things.

There are loads of juicy data you can get out of WooCommerce. There’s a nice piece on S|tack Overflow that shows how you can use get_items() to loop through the products in an order. You can also analyse tax breakdowns, discount coupon use, and more… All without needing to install yet more plugins. Win! 😎 👍

Leave a Comment

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