Manual operations are always a nightmare for WooCommerce store owners. Thankfully, a bit of code can help and actions that would normally take hours can be executed in a few seconds via PHP.
Today, we’ll take a look at a very edge case, but this can still be helpful to understand the code and re-adapt it to other scenarios. If as a store owner you tend to replace products or product lines, it’s possible that you may need to replace the old products with the new ones inside existing orders, retroactively.
It’s a one-off operation that could take hours if it had to be done manually, based on the number of existing orders. With this simple snippet, however, you can edit an unlimited number of orders, and let the code replace ordered items. So, let’s see how this is done. Enjoy!
PHP Snippet: Function to Bulk Replace Products In Existing Orders, Retroactively
Important Note and Disclaimer: this function could mess up your entire WordPress database. Use only if you know what you are doing and also test it first on staging.
Usage: simply append the “bb-update-orders” URL parameter to any WordPress admin URL (e.g. https://example.com/wp-admin/index.php?bb-update-orders), hit enter in the browser bar, and the function will trigger.
/**
* @snippet Bulk Replace Product @ WooCommerce Orders
* @how-to Get CustomizeWoo.com FREE
* @author Rodolfo Melogli
* @compatible WooCommerce 7
* @community https://businessbloomer.com/club/
*/
add_action( 'admin_init', 'bbloomer_update_old_orders' );
function bbloomer_update_old_orders() {
if ( isset( $_REQUEST['bb-update-orders'] ) ) {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'You do not have permission to bulk update orders', 'woocommerce' ) );
}
$old_product_id = 789;
$new_product_id = 123456;
$new_product = wc_get_product( $new_product_id );
$last_order = get_posts( 'post_type=shop_order&numberposts=1&fields=ids' );
$last_order_id = $last_order[0];
for ( $i = 1; $i <= $last_order_id; $i++ ) {
$order = wc_get_order( $i );
if ( ! $order instanceof WC_Order ) continue;
$replaced = false;
foreach ( $order->get_items() as $item_id => $item ) {
if ( $item && $item->get_product_id() == $old_product_id ) {
wc_delete_order_item( $item_id );
$order->add_product( $new_product, $item->get_quantity(), array( 'order' => $order ) );
$order->add_order_note( 'Replaced product programmatically' );
$replaced = true;
}
}
if ( $replaced ) $order->save();
}
}
}
Hmm, no luck for me :(. I put my hopeful use case below but I tried just two simple products and no order notes were made, etc.
The use case I wanted to try – WooCommerce Product Bundles
We have Bundle 1 with Product A and B (digital downloads).
Ten people purchase.
Later, we add Product C so Bundle 1 is now Product A, B, and C.
However the previous ten people do not have access to product C. Regenerating download permissions does not work.
I thought this might work so I tested running it with the same product ID. (i.e. remove Bundle 1, but add Bundle 1) but doesn’t appear to work in that case. I know it is outside the realm of what you were intending but thought I would report.
Thanks Shelley! Honestly I tried with simple products only, not sure how it goes for bundles.