In WooCommerce Subscriptions 4.9.0 we declared compatibility with WooCommerce’s new High-Performance Order Storage (HPOS) feature, which means it’s time for extensions that integrate with WooCommerce Subscriptions to update their code in order to be compatible with Subscriptions being stored in the new wc_orders
tables.
Getting Started:
Glossary:
- Subscription or Subscription object – an extension of the WooCommerce Order object with type
shop_subscription
with additional Subscriptions meta like billing period and interval, next payment dates and related orders. - Subscription product – the simple and variable Subscription products when purchased via the checkout turn into a Subscription.
- WC Subscriptions – this refers to the WooCommerce Subscriptions extension.
Subscriptions in HPOS
When HPOS is enabled, Order data is stored in new, dedicated order database tables. Similarly, because Subscriptions are an extension of the WC_Abstract_Order
class, its data is now also stored in the new dedicated order database tables.
With HPOS enabled, Subscription data will be found in the following tables:
wp_wc_orders
– Where the ID column is the Subscription ID and the type is set toshop_subscription
wp_wc_order_addresses
– Where theorder_id
column refers to a Subscription IDwp_wc_order_operational_data
– Where theorder_id
column refers to a subscription IDwp_wc_orders_meta
– Where theorder_id
column refers to a Subscription ID
This has changed from the previous structure where Subscriptions and Subscription metadata were stored in two Post tables:
wp_posts
– where ID is a Subscription ID andpost_type
is ‘shop_subscription
‘wp_post_meta
– where post_id refers to a Subscription
When it comes to migrating the Subscription data from WP Posts to the new HPOS tables, they are migrated automatically using the WooCommerce Core data synchronization feature.
After your subscriptions have been migrated, you may notice there are WP Posts with post type shop_order_placehold
with the same ID as a Subscription ID.
When HPOS is enabled and data syncing is disabled, Subscription post objects will be stored as shop_order_placehold
in the wp_posts
table. This post is used as a placeholder for the post ID (to ensure we don’t have Subscriptions in the new tables and other posts with the same ID in the posts table).
Changes in WC Subscriptions to Support HPOS
WC Subscriptions relied on a lot of code using WP Post APIs. Therefore, a big portion of our changes to support HPOS was to refactor our functions to use WC CRUD while also maintaining backward compatibility.
Aside from those changes, there were other additions, deprecations, and disabling of old features we had to change in order to fully support Subscriptions and Orders in HPOS, these include:
New functions & Helpers
wcs_get_orders_with_meta_query
– wrapper forwc_get_orders()
to search for orders or Subscriptions with meta query args in a backward compatible way.wcs_is_custom_order_tables_usage_enabled
– helper function to determine if HPOS is enabled on the store.wcs_is_custom_order_tables_data_sync_enabled
– helper function to determine if HPOS and data synchronization is enabled.
Deprecated/removed functions, hooks and filters
Function/Action/Filter/Property | Status | Type | Description |
woocommerce_new_subscription_data | Discouraged | Filter | This hook is called with arguments for wp_insert_post. This is no longer used when HPOS based Subscriptions are created so will not always be called. It will continue to work on stores with HPOS disabled. wcs_create_subscription can be used when a new subscription is created instead. |
wcs_{$copy_type}_meta_query | Deprecated | Dynamic Filter | Where copy_type is subscription, parent, renewal_order, resubscribe_order. We’ve maintained backwards compatibility but there’s no direct replacement for this hook. Use wc_subscriptions_copied_data or wc_subscriptions_{$copy_type}_data instead |
wcs_{$copy_type}_meta | Deprecated | Dynamic Filter | Where copy_type is subscription, parent, renewal_order, resubscribe_order. Replacement use ‘wc_subscriptions_copied_data’ or ‘wc_subscriptions_{$this->copy_type}_data’ instead |
wcs_subscriptions_for_{$relation_type}_order | Removed | Dynamic Filter | Where relation_type is renewal, switch or resubscribe. This hook was deprecated in v2.3.2 of WC Subscriptions (released on 10th July, 2018) |
woocommerce_subscriptions_admin_related_orders_to_display | Deprecated | Filter | This filter is deprecated in favour of ‘wcs_admin_subscription_related_orders_to_display’. This hook will not work with HPOS, since it accepts the order post object as an argument. It will continue to work on stores with HPOS disabled. |
woocommerce_subscriptions_related_orders_meta_box_rows | Deprecated | Hook | This action is deprecated in favour of ‘wcs_related_orders_meta_box_rows’. This action will not work with HPOS, since it accepts the order post object as an argument. It will continue to work with CPT. |
woocommerce_subscriptions_related_orders_meta_box | Deprecated | Hook | This action is deprecated in favour of ‘wcs_related_orders_meta_box’. This action will not work with HPOS, since it accepts the order post object as an argument. It will continue to work with CPT. |
woocommerce_subscriptions_related_orders_meta_box | Deprecated | Property | This object property is protected, so is only accessible to third-party code that extended the WCS_Post_Meta_Cache_Manager class. They should use the WCS_Post_Meta_Cache_Manager::object_data_cache_manager instead |
WC_Subscriptions_Switcher::update_shipping_methods() | Deprecated | Function | No replacement, also wasn’t used internally. |
WC_REST_Subscription_System_Status_Manager::add_subscription_fields_to_reponse | Deprecated | Function | Replaced by `WC_REST_Subscription_System_Status_Manager::add_subscription_fields_to_response()` to support HPOS and correct spelling error |
WC_Subscriptions_Cart::get_calculated_shipping_for_package | Deprecated | Function | This function was a wrapper for accessing shipping rates stored in a cache. The cache is no longer necesary and was impacting 3rd-party plugins |
WC_Subscriptions_Cart::cache_package_rates | Deprecated | Function | This function only cached the rates after they were calculated. Given the cache is no longer in use, this function served no purpose. |
Other low-level changes
- New Subscription data store –
WCS_Orders_Table_Subscription_Data_Store
class. An instance of this class is returned by callingWC_Data_Store::load( 'subscription' )
- New Object Meta Cache Manager – this is to replace our existing
WCS_Post_Meta_Cache_Manager
and is used when the store has HPOS enabled. This class is used to track metadata changes on orders and Subscriptions and trigger updated/deleted hooks to assist with keeping our cached data up-to-date.
Disabling of legacy features and code
Legacy Subscription reports have been disabled on stores that have HPOS without data syncing turned on. Subscription Reports currently contain complex direct DB queries which won’t work in HPOS environments unless the Subscription data is synced to posts meta tables. Rather than delaying our HPOS compatibility release, we decided to temporarily disable our reports while we work on updating them to support the new HPOS feature.
Old upgrade and repair scripts won’t work. WC Subscriptions has some old upgrade and repair scripts that use direct database queries and WP Post APIs to fetch and update Subscription data. Rather than upgrading and testing all of these old repair scripts to work in HPOS, we decided to disable them. This change will only affect stores that are updating from a very old version of Subscriptions (5+ years old) and have already migrated Subscriptions data to the new HPOS tables (extremely unlikely, if not impossible).
How to Update Your Subscription Integration Code
To support Subscriptions in HPOS tables, you will need to audit your Subscription integrations code for any direct DB queries or usage of WordPress APIs and replace them with the equivalent WC CRUD method or Data Store method
Most common WC CRUD and Data Store methods were introcued in WooCommerce Core 3.0 and do not need to be surrounded by any conditional version checks.
Follow the steps in the Auditing the code base for direct DB access usage section of the High Performance Order Storage Upgrade Recipe Book to find instances of direct DB queries or usage of WordPress APIs.
Example code changes:
Updating WP Post API functions with WC CRUD methods
get_post_meta( $subscription_id, '_billing_period', true );
Replacement:
// get an instance of the subscription if you don't already have one. $subscription = wcs_get_subscription( $subscription_id ); $subscription->get_meta( '_billing_period', true );
update_post_meta( $subscription_id, '_my_gateway_source_id', '_source_1234' );
Replacement:
// get an instance of the subscription if you don't already have one. $subscription = wcs_get_subscription( $subscription_id ); $subscription->update_meta_data( '_my_gateway_source_id', '_source_1234' ); $subscription->save();
get_edit_post_link( $subscription_id )
Replacement:
// get an instance of the subscription if you don't already have one. $subscription = wcs_get_subscription( $subscription_id ); $subscription->get_edit_order_url();
Updating WP Post Type checks with using the Data Store
'shop_subscription' === get_post_type( subscription_id ) // or 'shop_subscription' === $post->post_type
Replacement:
'shop_subscription' === WC_Data_Store::load( 'subscription' )->get_order_type( $object_id ).
For more examples, check out: APIs for getting/setting posts and postmeta.
Tip: a Subscription object is returned when you call wc_get_order( $subscription_id )
because a Subscription is a custom order type.
What code doesn’t need updating?
- Subscription simple and variable products
- Subscription order items
While updating your Subscriptions integration code, you may come across WordPress APIs being used on Subscription products or Subscription order items. Since products are remaining in the Posts table and orderitems already have their own tables, these areas of code should not need to be updated to support HPOS.
If in doubt, update the code to use WC CRUD methods, as this approach is data store agnostic and will future-proof your code for any upcoming database changes.
Leave a Reply