Blog

How to make Woocommerce more enterprise friendly

Hello!

After Working with WordPress and Woocommerce for a while, watching it grow and evolve over time, there are some constraints that we have been encountered over the years in particular with larger enterprise projects.

WordPress appeals to a massive audience (43% market share to be exact). In this massive appeal, the WordPress development community has to make careful decisions in order to balance the needs of the majority of users with the unique demands that enterprise implementations may have.

The most common requirements for enterprise WordPress sites would be : Redundancy, Deployment, Testing/QA and Security. All of these requirements are closely tied together in one way or another but they are all limited or hindered by the way WordPress is fundamentally designed.

This can usually be accommodated with standard static WordPress sites. The moment you delve into the e-commerce world with Automattic’s Woocommerce, you run into problems.

Why Woocommerce isnt enterprise ready out of the box

If you were to establish a method to copy your staging or development site to production, you would wipe out all your Woocommerce orders.

“Why not just backup or export the orders with the multitude of tools available?”, you might ask. Sure that will work. But all your order numbers will change. Any historical records of order numbers to reference completed or in-progress orders will be messed up.

When you are running an e-commerce site, this is completely unacceptable and potentially a huge disruption. Why is Woocommerce set up this way?

Much like most other things with wordpress, every plugin that implements any sort of custom content will leverage the wp_posts table. Every custom content is really just a WordPress post with extra meta fields (which live in the wp_postmeta table).

So these two tables are where most of the Woocommerce order data is stored. The key referencing identifier for any post in WordPress (custom post type or otherwise) is the post ID. The post ID is the primary key of this table. When you add a new post on your site, that will increment the post ID reference.

Everything else references this post ID to tie all that wonderful extensive data that is shown when you view an order in Woocommerce.

How to customize Woocommerce to use its own order ID system

This wasn’t as hard as you think. Before I get into the details of how to do this, I’ll reiterate the why. If we can decouple the reliance of Woocommerce with the primary key of wp_posts, then it can become that much more independent and allow you to export all orders when conducting a migration or site push (i.e. staging to production).

The only caveat I should note at this point is that I am assuming your Woocommerce store will only be using guest checkout. Customer (or user) data would also have to be backed up and restored in a similar way, which is possible but just adds an extra step.

Create custom meta data for historical Woocommerce Order numbers

We want to create a static order ID that just wont change if the post ID primary key structure changes within your WordPress mysql database. What we want to do first and foremost is populate this data as a wp_postmeta key (in our case called s8w_main_order_id) :

global $wpdb;
ini_set('max_execution_time', -1);
ini_set('memory_limit', -1);
$orders = wc_get_orders( array('numberposts' => -1) );

foreach ($orders as $order) {
    echo "Doing order #" . $order->id . "\n";
    $order->update_meta_data('_s8w_main_order_id', $order->id );
    $order->save();
}

In the snippet above (ultimately implemented as a wp-cli command) is iterate through all existing Woocommerce orders in the database and then populate the new meta key with the value of the current post ID. The idea here is that we have the order number structure thats already in place. We want to retain those order numbers for historical orders and then new order numbers will just be generated ad-hoc but using the new meta key as well so that future pushes from staging to production will not wipe them out as well.

Populate custom meta data for new Woocommerce orders that are created

So we covered historical orders in Woocommerce. Now we want all new orders created to populate this meta key automatically :

add_action('woocommerce_new_order', 'shift8_before_checkout_create_order', 20, 2);
function shift8_before_checkout_create_order( $order_id ) {
	$order = wc_get_order($order_id);
   	$order->update_meta_data('_s8w_main_order_id', $order->id );
   	$order->save();
}

Similarly to how we populated the meta value with historical orders, we are taking the post ID and just saving it in this extra meta key/value. This gets untouched moving forward if you fully understand whats happening here. We are simply decoupling the reliance on post IDs for orders.

Display our newly created order ID when viewing the order list in Woocommerce

The next few steps are to ensure there isn’t confusion when viewing or searching orders. We want this order ID reference to be as stable as possible. I should note that if you have a separate ERP or inventory management system, you might need to accommodate those systems to pull this newly created order ID instead.

add_filter( 'manage_edit-shop_order_columns', 'shift8_custom_shop_order_column', 12, 1 );
function shift8_custom_shop_order_column($columns)
{
    //Add the new column before anything & skip original order column
    $title = 'order_number';
    $shift8_columns = array();
    foreach ($columns as $key => $value) {
    	if ($key == $title) {
    		$shift8_columns['order_s8wid'] = 'test'.__( 'Order','woocommerce').''; // title
    		continue;
    	}
    	$shift8_columns[$key] = $value;
    }
    return $shift8_columns;
}

In the code above, we are swapping the existing order ID column with a new one of the exact same name. We then want to populate the data in this column using the meta key/value that we created earlier :

add_action( 'manage_shop_order_posts_custom_column' , 'shift8_custom_order_list_column_content', 10, 2 );
function shift8_custom_order_list_column_content( $column, $post_id )
{
    // Get the data from your custom field (set the correct meta key below)
    $s8w_orderid = get_post_meta( $post_id, '_s8w_main_order_id', true );
    $s8w_ordername = get_post_meta( $post_id, '_billing_first_name', true ) . ' ' . get_post_meta( $post_id, '_billing_last_name', true );

    if( empty($s8w_orderid)) $s8w_orderid = '';

    switch ( $column )
    {
        case 'order_s8wid' :
            echo ' #' . $s8w_orderid . ' ' . $s8w_ordername . ''; // display the data
            break;
    }
}

The code above again simply populates the data into the new order ID column, using the meta key/value data. There is one last thing we need to do. We need to hook into woocomomerce_order_number to swap any display of the order number with our meta key/value :

add_filter( 'woocommerce_order_number', 'shift8_change_woocommerce_order_number' );

function shift8_change_woocommerce_order_number( $order_id ) {
	$s8w_order = get_post_meta( $order_id, '_s8w_main_order_id', true );
    $new_order_id = $s8w_order;
    return $new_order_id;
}

Ensure the new meta order ID in Woocommerce is searchable

The last thing that we want to do is ensure that this new order ID is searchable if one were to want to look up an old order for whatever reason :

add_filter( 'woocommerce_shop_order_search_fields', 'shift8_search_fields', 10, 1 );
function shift8_search_fields( $meta_keys ){
    $meta_keys[] = '_s8w_main_order_id';
    return $meta_keys;
}

Summary

To summarize whats happening , we are :

  1. Creating a new meta key / value for wp_postmeta to associate a decoupled order ID system for Woocommerce.
  2. Populating all historical orders with the new key/value
  3. Populating all newly created orders with the new key/value
  4. Display this new order ID reference within Woocommerce (As well as searching orders) using this new system

This allows us to take Woocommerce one step closer to an enterprise ready scenario of being able to have CI/CD systems “push” your staging sites live. Decentralizing your WordPress admin to a secure separate instance will improve security, redundancy and reduce the likelihood of errors while increasing the QA and testing coverage of your site in general.

The process of automating the export and import of orders can now accommodate this new order ID without having any reliance on post ID structures changing. If you add a new post on your staging site, then push it live , it wont throw off all your order IDs as a result.

In our next post I’ll detail how to automate the push while leveraging what we have done here.