2.6.x to 3.0.0 Developer Migration Notes
Table of Contents
- Virtual orders do not have shipping addresses
- set_payment_method does not update meta values
- Data filters differ when extending classes with a different object type
- Notification email sending
- Order post dates
- Display of variable product and 'free' prices
- Product visibility is taxonomy based instead of meta based
- Removed "items" column in orders panel
- Removed product downloads update for past orders
- Automatic tax rate sorting
- Product schema markup moved to JSON LD format
- Auto-capture of authorised paypal payments
- Cart percent/Product percent coupon types merged
- Array return values being swapped out with objects implementing ArrayAccess
- Variation actions/filters prefixes
- Objects are passed by reference to actions/filters
- Variation IDs
- Select2 Version 4
- Order version property
- Order address indexes
- Template changes
- Deprecated functions
- Deprecated filters
Virtual orders do not have shipping addresses
In 2.6.x, orders containing virtual items only would copy the billing address to the shipping address on save.
In 3.0.x, orders containing virtual items will not have shipping addresses because the order is not shippable. Code that requires a shipping address should fall back to the billing address if not available.
To detect this, see if the order has shipping_address_2 or shipping_address_2 set, or use the
->has_shipping_address() method after 3.0.4.
When accessing this via an item object,
$item['item_meta'] will return an array of values under 2.6, but a single meta value under 3.0.7.
We recommend using
$item->get_meta() instead as accessing legacy data is not encouraged.
In 2.6.x this would return a negative count (bug). In 3.0.x this is a positive count representing how many units have been refunded.
set_payment_method does not update meta values
In 3.0.0, using
$order->set_payment_method() also requires a call to
$order->save() to persist the data.
Data filters differ when extending classes with a different object type
Filters in objects take the format:
For example, a width prop in a product object has the filter:
If you extending products and used a custom object type in place of
product, your filter name would differ.
Notification email sending
In 2.6.x, email notifications were sent in the same request as the order status change.
In 3.0.0, they are instead queued using WP Cron and send in a separate request.
If using actions to stop emails sending, your code will need to handle the new system. https://github.com/woocommerce/woocommerce/issues/13318
Order post dates
As pointed out in https://github.com/woocommerce/woocommerce/issues/13380, orders in 2.6.x would have their post dates updated when moving from pending to some other status.
In 3.0.0, the post date is the date the order was created. This will no longer change unless explicitly set.
Display of variable product and 'free' prices
Prices have been standardized in 3.0.0.
Variable products which show ranges and are on sale no longer show the striked out prices since these are long and confusing.
<del>$20.99 - $25.99</del> <ins>$10 - $12</ins>
$10 - $12
$0 price no longer display as free. This previously caused inconsistencies with ranges and with localization.
Product visibility is taxonomy based instead of meta based
3.0.0 instroduces a new product visibility taxonomy;
outofstock are terms. These are set on upgrade and help filter products in the catalog during frontend queries.
Removed "items" column in orders panel
This column previously showed a toggle list of items in the order for quick viewing, however, this was a performance bottleneck and has been removed. It was loading all items for all orders on screen; loading time is much improved with it gone.
Removed product downloads update for past orders
In 2.6.x if you edited a product's downloadable files, it would attempt to find all orders of said product and update download permissions.
This was not only a performance bottleneck, but also unexpected behavior as it would grant access to new files for old perchasers without prompting.
This has been removed in 3.0.0. Editing a file will however still update past purchases as it should.
Automatic tax rate sorting
In 2.6.x, the order in which tax rates were displayed and used were sortable with drag and drop.
In 3.0.0, manual sorting is removed. Tax rates will now sort logically based on the zip codes/cities/postcodes/priorities defined.
Product schema markup moved to JSON LD format
2.6.x showed output schema.org markup inline. This has implications if a theme changed markup or missed certain elements.
In 3.0.x, this is now output in the footer of the site in JSON LD format instead. For more details, see https://developers.google.com/schemas/formats/json-ld.
Auto-capture of authorised paypal payments
Payments authorized in 2.6.x using PayPal would not be captured at all and would require manual admin intervention to capture funds.
In 3.0.x, these funds will be captured automatically when the order is marked 'complete' using the PayPal API.
Cart percent/Product percent coupon types merged
Cart percent and
Product percent coupon types in 2.6.x. In 3.0.0 these are merged since they provide the exact same amount of discount regardless.
Array return values being swapped out with objects implementing ArrayAccess
In some places, 2.6.x returned arrays representing data such as taxes. In 3.0.0, proper classes have been added instead. These new classes implement ArrayAccess so old code trying to reference values in the 'array' still functions.
There is one caveat to this; if you check returned data to see if it's an array with
is_array this check will return
false since it's not an array in 3.0.0, it's an object. This will fail silently so needs to be checked in the extension code.
New classes implementing ArrayAccess as are follows:
This is a list of methods which returned arrays in 2.6.x, but objects in 3.0.0:
WC_Product::get_files()(returns an array of objects)
WC_Product::get_attributes()(returns an array of objects)
WC_Abstract_Order::get_items();(returns an array of objects)
WC_Abstract_Order::get_fees()(returns an array of objects)
WC_Abstract_Order::get_taxes()(returns an array of objects)
WC_Abstract_Order::get_shipping_methods()(returns an array of objects)
Variation actions/filters prefixes
woocommerce_get_regular_price/sale_price filters were in the abstract in 2.6.x. For products, these hooks are replaced with
woocommerce_get_product_sale_price and mapped so existing filters continue to function.
The exception is with product variations - these have their own hook prefix making them
woocommerce_get_product_variation_sale_price. There is no mapping in place for these filters.
Objects are passed by reference to actions/filters
If you're being passed a CRUD object via an action or filter hook, changes you make to said object will persist. If you need to change object properties without doing this, clone the variable.
// Create a clone to ensure item totals will not be modified permanently. $item = clone $item;
$variation->id was the ID of the parent product, and
$variation->variation_id was the ID of the variation. Backwards compatibility is maintained, but when moving to CRUD ensure you use the correct getters and setters.
$variation->get_id() is the variation ID.
$variation->get_parent_id() is the parent product ID.
Select2 Version 4
From WC Beta 2, we've migrated to Select2 V4.
Select2 V4 is mostly compatible with Select2 V3 with a few exceptions, the main one being how AJAX search inputs work. WooCommerce has two instances of these which are affected and need some HTML markup changes to function.
These inputs were 'hidden' text inputs in v3. In v4 these needs to be changed to regular multiselect elements.
<input type="hidden" id="grant_access_id" name="grant_access_id" data-multiple="true" class="wc-product-search" style="width: 400px;" data-placeholder="<?php esc_attr_e( 'Search for a downloadable product…', 'woocommerce' ); ?>" data-action="woocommerce_json_search_downloadable_products_and_variations" />
Would be changed to a regular multiselect field:
<select id="grant_access_id" class="wc-product-search" name="grant_access_id" multiple="multiple" style="width: 400px;" data-placeholder="<?php esc_attr_e( 'Search for a downloadable product…', 'woocommerce' ); ?>" data-action="woocommerce_json_search_downloadable_products_and_variations"></select>
Note that the field name in this case was changed from grant_access_id to grant_access_id.
data-multiple is no longer needed. The multiple prop is added instead. This supports multiple selections.
Handling the POSTed data
In the previous example, the hidden input would have posted a comma delimited string of IDs. With the switch to multi selects, this code needs to handle an array of values instead.
Handled in the same way as
Checking POSTed data
Unlike hidden inputs, if no selections are made multi-selects do not POST. Your code needs to check if the field
isset() to deal with posted values and handles scenarios where no selections are made.
Order version property
changing order line items via the admin interface would update them to the WC > 2.3.7 structure (in wc_save_order_items()), because of this, it would update the order version. This is the only time the version data is actually different between WC < 2.3.7 and 2.3.7-2.6.14 so it's the only time the order version if updated. changing order line items via any other method, be it the REST API, CLI or another API function (e.g. WC_Abstract_Order::add_tax()) would not definitively update the structure to the WC > 2.3.7 structure, so it would not update the order version. The calling code could update them to the newer structure, and if it chose to, it would be responsible for also updating the order version. changing any other data via any other method won't update the order version, because nothing has changed in terms of how the data is stored. tl;dr - the order version on orders with WC < 3.0.0 almost never changed, because the data structure almost never changed.
changing order line items via the admin interface would update them to the WC > 2.3.7 structure so it would update the order version (even if there are no changes to the structure). changing order line items via any other method, be it the REST API, CLI or another API function would update the structure to the WC > 3.0.0 structure, so it would update the order version (even if there are no changes to the structure). changing any other data via any other method would update the order version, even if nothing has changed in terms of how the order is being stored, but if something has changed, it will be updated. tl;dr - the order version on orders with WC 3.0.0+ will always change, even if the data structure hasn't changed, but the order version will also always reflect the version of the structure.
Order address indexes
Order address indexes are introduced in WooCommerce 3.0 to improve search results in the wp-admin, those indexes should be created automatically when an order is created or have changes on the address, although orders placed before 3.0 shouldn't contain any indexes, so in order to generate missing you can use the "Order address indexes" tool that can be found in "WooCommerce" > "Tools", this tool is also avaliable on WP-CLI with the follow command:
wp wc tool run add_order_indexes --user=<USER_ID>
Note: Since WooCommerce 3.6 this tool has been converted into a plugin: https://github.com/woocommerce/woocommerce-recreate-orders-indexes
|cart/cross-sells.php||Removes WP_Query for performance reasons.|
|myaccount/downloads.php||Improved download table layout.|
|single-product/add-to-cart/grouped.php||Checks grouped products exist correct, and CRUD compatibility.|
|single-product/photoswipe.php||Handles the new photoswipe gallery.|
|single-product/product-image.php||New image gallery.|
|single-product/product-thumbnails.php||New image gallery.|
|single-product/related.php||Removes WP_Query for performance reasons.|
|single-product/up-sells.php||Removes WP_Query for performance reasons.|
|single-product/stock.php||New template to output stock HTML.|
- All methods in the legacy (backwards compatibility) classes:
|woocommerce_single_product_image_html||Removed - new gallery has not 'single' image. All images are ran through woocommerce_single_product_image_thumbnail_html|