From 61c7abce4f50146fa595f6429e8f8d6fc082d9fc Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Thu, 23 Nov 2017 12:47:53 -0200 Subject: [PATCH 01/40] First pass at using post_author instead of the post meta '_customer_user' --- includes/class-wc-install.php | 1 + .../abstract-wc-order-data-store-cpt.php | 20 +++++++++++++++++-- .../class-wc-order-data-store-cpt.php | 9 ++++----- includes/wc-update-functions.php | 15 ++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 94246017a04..f9fc0b566a6 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -99,6 +99,7 @@ class WC_Install { '3.3.0' => array( 'wc_update_330_image_options', 'wc_update_330_set_default_product_cat', + 'wc_update_330_order_customer_id', 'wc_update_330_db_version', ), ); diff --git a/includes/data-stores/abstract-wc-order-data-store-cpt.php b/includes/data-stores/abstract-wc-order-data-store-cpt.php index 5e63c2caa6e..5842c166938 100644 --- a/includes/data-stores/abstract-wc-order-data-store-cpt.php +++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php @@ -69,7 +69,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_type' => $order->get_type( 'edit' ), 'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ), 'ping_status' => 'closed', - 'post_author' => 1, + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 1, 'post_title' => $this->get_post_title(), 'post_password' => uniqid( 'order_' ), 'post_parent' => $order->get_parent_id( 'edit' ), @@ -137,7 +137,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme $changes = $order->get_changes(); // Only update the post when the post data changes. - if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'parent_id', 'post_excerpt' ), array_keys( $changes ) ) ) { + if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'parent_id', 'post_excerpt', 'customer_id' ), array_keys( $changes ) ) ) { $post_data = array( 'post_date' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getOffsetTimestamp() ), 'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ), @@ -146,6 +146,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_excerpt' => $this->get_post_excerpt( $order ), 'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ), 'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ), + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 1, ); /** @@ -163,12 +164,27 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme wp_update_post( array_merge( array( 'ID' => $order->get_id() ), $post_data ) ); } $order->read_meta_data( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook. + + // If customer changed, update any downloadable permissions. + if ( in_array( 'customer_id', $changes ) ) { + $this->update_downloadable_permissions( $order ); + } } $this->update_post_meta( $order ); $order->apply_changes(); $this->clear_caches( $order ); } + /** + * Update downloadable permissions for a given order. + * + * @param WC_Order $order Order object. + */ + protected function update_downloadable_permissions( $order ) { + $data_store = WC_Data_Store::load( 'customer-download' ); + $data_store->update_user_by_order_id( $order->get_id(), $order->get_customer_id(), $order->get_billing_email() ); + } + /** * Method to delete an order from the database. * diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index fa3724dea32..1e838aeee0f 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -110,7 +110,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $order->set_props( array( 'order_key' => get_post_meta( $id, '_order_key', true ), - 'customer_id' => get_post_meta( $id, '_customer_user', true ), + 'customer_id' => $post_object->post_author, 'billing_first_name' => get_post_meta( $id, '_billing_first_name', true ), 'billing_last_name' => get_post_meta( $id, '_billing_last_name', true ), 'billing_company' => get_post_meta( $id, '_billing_company', true ), @@ -258,10 +258,9 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement update_post_meta( $id, '_shipping_address_index', implode( ' ', $order->get_address( 'shipping' ) ) ); } - // If customer changed, update any downloadable permissions. - if ( in_array( 'customer_user', $updated_props ) || in_array( 'billing_email', $updated_props ) ) { - $data_store = WC_Data_Store::load( 'customer-download' ); - $data_store->update_user_by_order_id( $id, $order->get_customer_id(), $order->get_billing_email() ); + // If customer email changed, update any downloadable permissions. + if ( in_array( 'billing_email', $updated_props ) ) { + $this->update_downloadable_permissions( $order ); } do_action( 'woocommerce_order_object_updated_props', $order, $updated_props ); diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 3c188a49a33..fca7530b206 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1507,6 +1507,21 @@ function wc_update_330_set_default_product_cat() { } } +/** + * Copy order customer_id from post meta to post_author. + */ +function wc_update_330_order_customer_id() { + global $wpdb; + + $orders_to_update = $wpdb->get_results( + "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE meta_key = '_customer_user' AND p.post_type = 'shop_order'" + ); + + foreach ( $orders_to_update as $order ) { + $wpdb->update( $wpdb->posts, array( 'post_author' => $order->customer_id ), array( 'ID' => $order->post_id ) ); + } +} + /** * Update DB Version. */ From ad4304450d0ef2eda621f562d67f6e9f39c2bb1a Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Fri, 8 Dec 2017 12:32:45 -0200 Subject: [PATCH 02/40] Set post_author to 0 for refund orders --- includes/data-stores/abstract-wc-order-data-store-cpt.php | 4 ++-- includes/wc-update-functions.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/includes/data-stores/abstract-wc-order-data-store-cpt.php b/includes/data-stores/abstract-wc-order-data-store-cpt.php index 5842c166938..53dccfe8bc8 100644 --- a/includes/data-stores/abstract-wc-order-data-store-cpt.php +++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php @@ -69,7 +69,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_type' => $order->get_type( 'edit' ), 'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ), 'ping_status' => 'closed', - 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 1, + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 0, 'post_title' => $this->get_post_title(), 'post_password' => uniqid( 'order_' ), 'post_parent' => $order->get_parent_id( 'edit' ), @@ -146,7 +146,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_excerpt' => $this->get_post_excerpt( $order ), 'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ), 'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ), - 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 1, + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 0, ); /** diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 57e471713dd..b03bc08488c 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1544,7 +1544,7 @@ function wc_update_330_set_default_product_cat() { } /** - * Copy order customer_id from post meta to post_author. + * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. */ function wc_update_330_order_customer_id() { global $wpdb; @@ -1556,6 +1556,8 @@ function wc_update_330_order_customer_id() { foreach ( $orders_to_update as $order ) { $wpdb->update( $wpdb->posts, array( 'post_author' => $order->customer_id ), array( 'ID' => $order->post_id ) ); } + + $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); } /** From 997a6fa40dab53247659ae5919895e9bbe0f47b9 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Fri, 8 Dec 2017 15:55:53 -0200 Subject: [PATCH 03/40] Make sure post_author is used only after WC 3.3.0 DB upgrade routine is executed --- includes/data-stores/class-wc-order-data-store-cpt.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index 1e838aeee0f..2254b4d5859 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -107,10 +107,17 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $date_paid = get_post_meta( $id, '_paid_date', true ); } + // Make sure post_author is used only after WC 3.3.0 DB upgrade routine is executed. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.3.0', '>=' ) ) { + $customer_id = $post_object->post_author; + } else { + $customer_id = get_post_meta( $id, '_customer_user', true ); + } + $order->set_props( array( 'order_key' => get_post_meta( $id, '_order_key', true ), - 'customer_id' => $post_object->post_author, + 'customer_id' => $customer_id, 'billing_first_name' => get_post_meta( $id, '_billing_first_name', true ), 'billing_last_name' => get_post_meta( $id, '_billing_last_name', true ), 'billing_company' => get_post_meta( $id, '_billing_company', true ), From 6ecc3bc44229cf8f11bc071fd1546379a11f6a5d Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 11 Dec 2017 10:24:19 -0200 Subject: [PATCH 04/40] Use post_author field instead of post meta in orders admin page This commit changes the query used to get a list of orders in the orders admin page to use post_author field instead of the _customer_user post meta when filtering orders by customer. Testing this change on a local WooCommerce install with about 2.5 million entries in the wp_posts table and about 100 million entries in the wp_postmeta table I got the following results: - The original query using _customer_user took 28 seconds to run - The new query using post_author took 0.0008 seconds to run --- .../class-wc-admin-list-table-orders.php | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/includes/admin/list-tables/class-wc-admin-list-table-orders.php b/includes/admin/list-tables/class-wc-admin-list-table-orders.php index 160ce1db439..45d4180944d 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-orders.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-orders.php @@ -712,15 +712,21 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table { // Filter the orders by the posted customer. if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok. - // @codingStandardsIgnoreStart - $query_vars['meta_query'] = array( - array( - 'key' => '_customer_user', - 'value' => (int) $_GET['_customer_user'], // WPCS: input var ok, sanitization ok. - 'compare' => '=', - ), - ); - // @codingStandardsIgnoreEnd + $customer_id = (int) $_GET['_customer_user']; // WPCS: input var ok, sanitization ok. + + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.3.0', '>=' ) ) { + $query_vars['author'] = $customer_id; + } else { + // @codingStandardsIgnoreStart + $query_vars['meta_query'] = array( + array( + 'key' => '_customer_user', + 'value' => $customer_id, + 'compare' => '=', + ), + ); + // @codingStandardsIgnoreEnd + } } // Sorting. From bf5c6f17a79087c77aa787cea4101ed2dead4a36 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 9 Jan 2018 15:23:42 -0200 Subject: [PATCH 05/40] phpcs fixes --- includes/admin/list-tables/class-wc-admin-list-table-orders.php | 2 -- includes/class-wc-install.php | 2 -- includes/data-stores/abstract-wc-order-data-store-cpt.php | 2 -- includes/data-stores/class-wc-order-data-store-cpt.php | 2 -- includes/wc-update-functions.php | 2 -- 5 files changed, 10 deletions(-) diff --git a/includes/admin/list-tables/class-wc-admin-list-table-orders.php b/includes/admin/list-tables/class-wc-admin-list-table-orders.php index 93819a68107..699fdca97b0 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-orders.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-orders.php @@ -2,8 +2,6 @@ /** * List tables: orders. * - * @author WooCommerce - * @category Admin * @package WooCommerce/Admin * @version 3.3.0 */ diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 6357e4dc515..ede0ae44d77 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -2,8 +2,6 @@ /** * Installation related functions and actions. * - * @author WooThemes - * @category Admin * @package WooCommerce/Classes * @version 3.0.0 */ diff --git a/includes/data-stores/abstract-wc-order-data-store-cpt.php b/includes/data-stores/abstract-wc-order-data-store-cpt.php index 53dccfe8bc8..dab4f0970ec 100644 --- a/includes/data-stores/abstract-wc-order-data-store-cpt.php +++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php @@ -13,8 +13,6 @@ if ( ! defined( 'ABSPATH' ) ) { * Abstract Order Data Store: Stored in CPT. * * @version 3.0.0 - * @category Class - * @author WooThemes */ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store_Interface, WC_Abstract_Order_Data_Store_Interface { diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index 2254b4d5859..c0cb70b136c 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -13,8 +13,6 @@ if ( ! defined( 'ABSPATH' ) ) { * WC Order Data Store: Stored in CPT. * * @version 3.0.0 - * @category Class - * @author WooThemes */ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implements WC_Object_Data_Store_Interface, WC_Order_Data_Store_Interface { diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index b03bc08488c..c8cb6284866 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -4,8 +4,6 @@ * * Functions for updating data, used by the background updater. * - * @author WooThemes - * @category Core * @package WooCommerce/Functions * @version 2.6.0 */ From 7f43c39f2fd1ee0c0e2dc47ac0badbd749416f8b Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 9 Jan 2018 15:23:58 -0200 Subject: [PATCH 06/40] Bump WC version to 3.4.0 --- .../admin/list-tables/class-wc-admin-list-table-orders.php | 2 +- includes/class-wc-install.php | 4 +++- includes/data-stores/class-wc-order-data-store-cpt.php | 4 ++-- includes/wc-update-functions.php | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/includes/admin/list-tables/class-wc-admin-list-table-orders.php b/includes/admin/list-tables/class-wc-admin-list-table-orders.php index 699fdca97b0..f71857dfa03 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-orders.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-orders.php @@ -743,7 +743,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table { if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok. $customer_id = (int) $_GET['_customer_user']; // WPCS: input var ok, sanitization ok. - if ( version_compare( get_option( 'woocommerce_db_version' ), '3.3.0', '>=' ) ) { + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.4.0', '>=' ) ) { $query_vars['author'] = $customer_id; } else { // @codingStandardsIgnoreStart diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index ede0ae44d77..5af76f292c8 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -97,10 +97,12 @@ class WC_Install { 'wc_update_330_webhooks', 'wc_update_330_product_stock_status', 'wc_update_330_set_default_product_cat', - 'wc_update_330_order_customer_id', 'wc_update_330_clear_transients', 'wc_update_330_db_version', ), + '3.4.0' => array( + 'wc_update_340_order_customer_id', + ), ); /** diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index c0cb70b136c..dc1a009cd88 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -105,8 +105,8 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $date_paid = get_post_meta( $id, '_paid_date', true ); } - // Make sure post_author is used only after WC 3.3.0 DB upgrade routine is executed. - if ( version_compare( get_option( 'woocommerce_db_version' ), '3.3.0', '>=' ) ) { + // Make sure post_author is used only after WC 3.4.0 DB upgrade routine is executed. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.4.0', '>=' ) ) { $customer_id = $post_object->post_author; } else { $customer_id = get_post_meta( $id, '_customer_user', true ); diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index c8cb6284866..2135466db7d 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1544,7 +1544,7 @@ function wc_update_330_set_default_product_cat() { /** * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. */ -function wc_update_330_order_customer_id() { +function wc_update_340_order_customer_id() { global $wpdb; $orders_to_update = $wpdb->get_results( From 1f39f7d63378caa86002795f19d2dae8890dc971 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Fri, 19 Jan 2018 13:45:30 -0200 Subject: [PATCH 07/40] Add filter to list of post_types used to build the query to copy _customer_user to post_author --- includes/wc-update-functions.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 2135466db7d..7f3830f83ef 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1547,8 +1547,16 @@ function wc_update_330_set_default_product_cat() { function wc_update_340_order_customer_id() { global $wpdb; + $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); + $query_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); + $orders_to_update = $wpdb->get_results( - "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE meta_key = '_customer_user' AND p.post_type = 'shop_order'" + $wpdb->prepare( + "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm + LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE meta_key = '_customer_user' AND p.post_type IN ({$query_placeholders})", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $post_types + ) ); foreach ( $orders_to_update as $order ) { From 35998e56cbe3939cb7a2eae2ce90c9a2b137bcb1 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Wed, 11 Apr 2018 15:23:12 -0300 Subject: [PATCH 08/40] Reposition wc_update_340_order_customer_id() function So that it is listed together with WC 3.3 update functions --- includes/wc-update-functions.php | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index b0e8517a364..8b22c4b0a38 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1563,31 +1563,6 @@ function wc_update_330_set_default_product_cat() { } } -/** - * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. - */ -function wc_update_340_order_customer_id() { - global $wpdb; - - $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); - $query_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); - - $orders_to_update = $wpdb->get_results( - $wpdb->prepare( - "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm - LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id - WHERE meta_key = '_customer_user' AND p.post_type IN ({$query_placeholders})", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared - $post_types - ) - ); - - foreach ( $orders_to_update as $order ) { - $wpdb->update( $wpdb->posts, array( 'post_author' => $order->customer_id ), array( 'ID' => $order->post_id ) ); - } - - $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); -} - /** * Update product stock status to use the new onbackorder status. */ @@ -1703,6 +1678,31 @@ function wc_update_340_irish_states() { } } +/** + * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. + */ +function wc_update_340_order_customer_id() { + global $wpdb; + + $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); + $query_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); + + $orders_to_update = $wpdb->get_results( + $wpdb->prepare( + "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm + LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE meta_key = '_customer_user' AND p.post_type IN ({$query_placeholders})", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $post_types + ) + ); + + foreach ( $orders_to_update as $order ) { + $wpdb->update( $wpdb->posts, array( 'post_author' => $order->customer_id ), array( 'ID' => $order->post_id ) ); + } + + $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); +} + /** * Update DB Version. */ From c77b2f20f443647fd09c0de9705d2dbc214548c3 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 16 Apr 2018 09:40:38 -0300 Subject: [PATCH 09/40] Change WC_Background_Updater to allow in-task resource monitoring This change makes it possible to monitor resource usage inside each of the database update functions. Useful when doing resource intensive updates to stop the execution and requeue before the execution is aborted. --- includes/class-wc-background-updater.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-background-updater.php b/includes/class-wc-background-updater.php index 6208871ac5d..8fb493a78a3 100644 --- a/includes/class-wc-background-updater.php +++ b/includes/class-wc-background-updater.php @@ -99,18 +99,26 @@ class WC_Background_Updater extends WC_Background_Process { wc_maybe_define_constant( 'WC_UPDATING', true ); $logger = wc_get_logger(); + $result = null; include_once dirname( __FILE__ ) . '/wc-update-functions.php'; if ( is_callable( $callback ) ) { $logger->info( sprintf( 'Running %s callback', $callback ), array( 'source' => 'wc_db_updates' ) ); - call_user_func( $callback ); - $logger->info( sprintf( 'Finished %s callback', $callback ), array( 'source' => 'wc_db_updates' ) ); + $result = call_user_func( $callback, $this ); + + if ( -1 === $result ) { + $message = sprintf( 'Requeuing %s callback.', $callback ); + } else { + $message = sprintf( 'Finished %s callback.', $callback ); + } + + $logger->info( $message, array( 'source' => 'wc_db_updates' ) ); } else { $logger->notice( sprintf( 'Could not find %s callback', $callback ), array( 'source' => 'wc_db_updates' ) ); } - return false; + return -1 === $result ? $callback : false; } /** @@ -125,4 +133,13 @@ class WC_Background_Updater extends WC_Background_Process { WC_Install::update_db_version(); parent::complete(); } + + /** + * See if the batch limit has been exceeded. + * + * @return bool + */ + public function is_batch_limit_exceeded() { + return $this->batch_limit_exceeded(); + } } From 4123cc6f7de74176a325e9f316784c25fb12eff9 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 16 Apr 2018 11:15:42 -0300 Subject: [PATCH 10/40] Migrate _customer_user to post_author in batches This will make the update slower but safer when running in cheap servers. --- includes/wc-update-functions.php | 79 ++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 8b22c4b0a38..1e1b849f0bb 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1680,27 +1680,86 @@ function wc_update_340_irish_states() { /** * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. + * + * @param WC_Background_Updater $updater Background updater instance. */ -function wc_update_340_order_customer_id() { +function wc_update_340_order_customer_id( $updater ) { global $wpdb; - $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); - $query_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); + $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); + $post_types_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); + $admin_orders_sql = ''; - $orders_to_update = $wpdb->get_results( + // Get the list of orders that we don't want to change as they belong to user ID 1. + $admin_orders = $wpdb->get_col( $wpdb->prepare( - "SELECT post_id, meta_value AS customer_id FROM {$wpdb->postmeta} pm - LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id - WHERE meta_key = '_customer_user' AND p.post_type IN ({$query_placeholders})", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + "SELECT ID FROM wp_posts p + INNER JOIN wp_postmeta pm ON p.ID = pm.post_id + WHERE post_type IN ({$post_types_placeholders}) AND meta_key = '_customer_user' AND meta_value = 1", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared $post_types ) ); - foreach ( $orders_to_update as $order ) { - $wpdb->update( $wpdb->posts, array( 'post_author' => $order->customer_id ), array( 'ID' => $order->post_id ) ); + if ( ! empty( $admin_orders ) ) { + $admin_orders_sql .= ' AND ID NOT IN (' . implode( ', ', $admin_orders ) . ') '; } - $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); + // Query to get a batch of orders IDs to change. + $query = $wpdb->prepare( + // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared + "SELECT ID FROM {$wpdb->posts} + WHERE post_author = 1 AND post_type IN ({$post_types_placeholders}) $admin_orders_sql + LIMIT 1000", + $post_types + // phpcs:enable + ); + + while ( true ) { + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $orders_to_update = $wpdb->get_col( $query ); + + // Exit loop if no more orders to update. + if ( ! $orders_to_update ) { + break; + } + + $orders_meta_data = $wpdb->get_results( + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + "SELECT post_id, meta_value as customer_id FROM {$wpdb->postmeta} WHERE meta_key = '_customer_user' AND post_id IN (" . implode( ', ', $orders_to_update ) . ')' + ); + + // Exit loop if no _customer_user metas exist for the list of orders to update. + if ( ! $orders_meta_data ) { + break; + } + + // Update post_author for a batch of orders. + foreach ( $orders_meta_data as $order_meta ) { + // Stop update execution and re-enqueue it if near memory and timeout limits. + if ( $updater->is_batch_limit_exceeded() ) { + return -1; + } + + $wpdb->update( $wpdb->posts, array( 'post_author' => $order_meta->customer_id ), array( 'ID' => $order_meta->post_id ) ); + + wp_cache_delete( $order_meta->post_id, 'posts' ); + wp_cache_delete( $order_meta->post_id, 'post_meta' ); + } + } + + // Set post_author to 0 instead of 1 to all shop_order_refunds. + while ( true ) { + // Stop update execution and re-enqueue it if near memory and timeout limits. + if ( $updater->is_batch_limit_exceeded() ) { + return -1; + } + + $updated_rows = $wpdb->query( "UPDATE {$wpdb->posts} SET post_author = 0 WHERE post_type = 'shop_order_refund' LIMIT 1000" ); + + if ( ! $updated_rows ) { + break; + } + } } /** From ccf10f6ab279902f1ce748df63cee1f769835f26 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 16 Apr 2018 15:04:57 -0300 Subject: [PATCH 11/40] Punt to WC version 3.5.0 --- .../class-wc-admin-list-table-orders.php | 3 ++- includes/class-wc-install.php | 4 +++- .../class-wc-order-data-store-cpt.php | 4 ++-- includes/wc-update-functions.php | 18 +++++++++--------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/includes/admin/list-tables/class-wc-admin-list-table-orders.php b/includes/admin/list-tables/class-wc-admin-list-table-orders.php index 0786d083098..463a0b435fd 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-orders.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-orders.php @@ -751,7 +751,8 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table { if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok. $customer_id = (int) $_GET['_customer_user']; // WPCS: input var ok, sanitization ok. - if ( version_compare( get_option( 'woocommerce_db_version' ), '3.4.0', '>=' ) ) { + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { $query_vars['author'] = $customer_id; } else { // @codingStandardsIgnoreStart diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index e73b9571c79..e985b82bce4 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -100,10 +100,12 @@ class WC_Install { 'wc_update_330_db_version', ), '3.4.0' => array( - 'wc_update_340_order_customer_id', 'wc_update_340_irish_states', 'wc_update_340_db_version', ), + '3.5.0' => array( + 'wc_update_350_order_customer_id', + ), ); /** diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index 1ebb2b42e2f..02b68b44bb7 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -107,8 +107,8 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $date_paid = get_post_meta( $id, '_paid_date', true ); } - // Make sure post_author is used only after WC 3.4.0 DB upgrade routine is executed. - if ( version_compare( get_option( 'woocommerce_db_version' ), '3.4.0', '>=' ) ) { + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { $customer_id = $post_object->post_author; } else { $customer_id = get_post_meta( $id, '_customer_user', true ); diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 1e1b849f0bb..e65d280db17 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1678,15 +1678,22 @@ function wc_update_340_irish_states() { } } +/** + * Update DB Version. + */ +function wc_update_340_db_version() { + WC_Install::update_db_version( '3.4.0' ); +} + /** * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. * * @param WC_Background_Updater $updater Background updater instance. */ -function wc_update_340_order_customer_id( $updater ) { +function wc_update_350_order_customer_id( $updater ) { global $wpdb; - $post_types = (array) apply_filters( 'woocommerce_update_340_order_customer_id_post_types', array( 'shop_order' ) ); + $post_types = (array) apply_filters( 'woocommerce_update_350_order_customer_id_post_types', array( 'shop_order' ) ); $post_types_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); $admin_orders_sql = ''; @@ -1761,10 +1768,3 @@ function wc_update_340_order_customer_id( $updater ) { } } } - -/** - * Update DB Version. - */ -function wc_update_340_db_version() { - WC_Install::update_db_version( '3.4.0' ); -} From e02f3fbec47dbc00a8d4b657d06490ba36591993 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 17 Apr 2018 11:05:46 -0300 Subject: [PATCH 12/40] Migrate data to post_author in a single go when using WP-CLI --- includes/wc-update-functions.php | 152 ++++++++++++++++++------------- 1 file changed, 91 insertions(+), 61 deletions(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index e65d280db17..405f3103fd6 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1688,83 +1688,113 @@ function wc_update_340_db_version() { /** * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds. * - * @param WC_Background_Updater $updater Background updater instance. + * Two different strategies are used to copy data depending if the update is being executed from + * the command line or not. If `wp wc update` is used to update the database, this function + * copies data in a single go that is faster but uses more resources. If the databse update was + * triggered from the wp-admin, this function copies data in batches which is slower but uses + * few resources and thus is less likely to fail on smaller servers. + * + * @param WC_Background_Updater|false $updater Background updater instance or false if function is called from `wp wc update` WP-CLI command. */ -function wc_update_350_order_customer_id( $updater ) { +function wc_update_350_order_customer_id( $updater = false ) { global $wpdb; $post_types = (array) apply_filters( 'woocommerce_update_350_order_customer_id_post_types', array( 'shop_order' ) ); $post_types_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); - $admin_orders_sql = ''; - // Get the list of orders that we don't want to change as they belong to user ID 1. - $admin_orders = $wpdb->get_col( - $wpdb->prepare( - "SELECT ID FROM wp_posts p - INNER JOIN wp_postmeta pm ON p.ID = pm.post_id - WHERE post_type IN ({$post_types_placeholders}) AND meta_key = '_customer_user' AND meta_value = 1", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared - $post_types - ) - ); - - if ( ! empty( $admin_orders ) ) { - $admin_orders_sql .= ' AND ID NOT IN (' . implode( ', ', $admin_orders ) . ') '; - } - - // Query to get a batch of orders IDs to change. - $query = $wpdb->prepare( - // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared - "SELECT ID FROM {$wpdb->posts} - WHERE post_author = 1 AND post_type IN ({$post_types_placeholders}) $admin_orders_sql - LIMIT 1000", - $post_types - // phpcs:enable - ); - - while ( true ) { - // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared - $orders_to_update = $wpdb->get_col( $query ); - - // Exit loop if no more orders to update. - if ( ! $orders_to_update ) { - break; - } - - $orders_meta_data = $wpdb->get_results( - // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared - "SELECT post_id, meta_value as customer_id FROM {$wpdb->postmeta} WHERE meta_key = '_customer_user' AND post_id IN (" . implode( ', ', $orders_to_update ) . ')' + if ( defined( 'WP_CLI' ) && WP_CLI ) { + // If running the update from the command-line, copy data in a single go which is faster but uses more resources. + $wpdb->query( + 'CREATE TEMPORARY TABLE customers_map (post_id BIGINT(20), customer_id BIGINT(20), PRIMARY KEY(post_id))' ); - // Exit loop if no _customer_user metas exist for the list of orders to update. - if ( ! $orders_meta_data ) { - break; + $wpdb->query( + "INSERT IGNORE INTO customers_map (SELECT post_id, meta_value FROM wp_postmeta WHERE meta_key = '_customer_user')" + ); + + $wpdb->query( 'SET sql_safe_updates=1' ); + + $wpdb->query( + $wpdb->prepare( + "UPDATE wp_posts JOIN customers_map ON wp_posts.ID = customers_map.post_id SET wp_posts.post_author = customers_map.customer_id WHERE post_type IN ({$post_types_placeholders})", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $post_types + ) + ); + + $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); + } else { + // If running the update from the wp-admin, copy data in batches being careful not to use more memory than allowed and respecting PHP time limit. + $admin_orders_sql = ''; + + // Get the list of orders that we don't want to change as they belong to user ID 1. + $admin_orders = $wpdb->get_col( + $wpdb->prepare( + "SELECT ID FROM wp_posts p + INNER JOIN wp_postmeta pm ON p.ID = pm.post_id + WHERE post_type IN ({$post_types_placeholders}) AND meta_key = '_customer_user' AND meta_value = 1", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $post_types + ) + ); + + if ( ! empty( $admin_orders ) ) { + $admin_orders_sql .= ' AND ID NOT IN (' . implode( ', ', $admin_orders ) . ') '; } - // Update post_author for a batch of orders. - foreach ( $orders_meta_data as $order_meta ) { + // Query to get a batch of orders IDs to change. + $query = $wpdb->prepare( + // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared + "SELECT ID FROM {$wpdb->posts} + WHERE post_author = 1 AND post_type IN ({$post_types_placeholders}) $admin_orders_sql + LIMIT 1000", + $post_types + // phpcs:enable + ); + + while ( true ) { + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $orders_to_update = $wpdb->get_col( $query ); + + // Exit loop if no more orders to update. + if ( ! $orders_to_update ) { + break; + } + + $orders_meta_data = $wpdb->get_results( + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + "SELECT post_id, meta_value as customer_id FROM {$wpdb->postmeta} WHERE meta_key = '_customer_user' AND post_id IN (" . implode( ', ', $orders_to_update ) . ')' + ); + + // Exit loop if no _customer_user metas exist for the list of orders to update. + if ( ! $orders_meta_data ) { + break; + } + + // Update post_author for a batch of orders. + foreach ( $orders_meta_data as $order_meta ) { + // Stop update execution and re-enqueue it if near memory and timeout limits. + if ( $updater instanceof WC_Background_Updater && $updater->is_batch_limit_exceeded() ) { + return -1; + } + + $wpdb->update( $wpdb->posts, array( 'post_author' => $order_meta->customer_id ), array( 'ID' => $order_meta->post_id ) ); + } + } + + // Set post_author to 0 instead of 1 on all shop_order_refunds. + while ( true ) { // Stop update execution and re-enqueue it if near memory and timeout limits. - if ( $updater->is_batch_limit_exceeded() ) { + if ( $updater instanceof WC_Background_Updater && $updater->is_batch_limit_exceeded() ) { return -1; } - $wpdb->update( $wpdb->posts, array( 'post_author' => $order_meta->customer_id ), array( 'ID' => $order_meta->post_id ) ); + $updated_rows = $wpdb->query( "UPDATE {$wpdb->posts} SET post_author = 0 WHERE post_type = 'shop_order_refund' LIMIT 1000" ); - wp_cache_delete( $order_meta->post_id, 'posts' ); - wp_cache_delete( $order_meta->post_id, 'post_meta' ); + if ( ! $updated_rows ) { + break; + } } } - // Set post_author to 0 instead of 1 to all shop_order_refunds. - while ( true ) { - // Stop update execution and re-enqueue it if near memory and timeout limits. - if ( $updater->is_batch_limit_exceeded() ) { - return -1; - } - - $updated_rows = $wpdb->query( "UPDATE {$wpdb->posts} SET post_author = 0 WHERE post_type = 'shop_order_refund' LIMIT 1000" ); - - if ( ! $updated_rows ) { - break; - } - } + wp_cache_flush(); } + From 923eeb2e43130575899823f078e6d0d86b3afe2f Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 17 Apr 2018 15:16:05 -0300 Subject: [PATCH 13/40] Monitor only memory limit while running the data migration Turns out WP_Background_Process uses an arbitrary value of 20 seconds of wall clock time to measure PHP "time limit" (https://github.com/woocommerce/woocommerce/blob/b0617a13c4ddba13559eaac1f295c9622b333523/includes/libraries/wp-background-process.php#L385) instead of cpu time which apparently it is not easy to measure in PHP. Since system calls (like database calls) are not included in the PHP maximum execution time, 20 seconds of wall clock is often way less than PHP time limit. Thus, this commit removes the call to WP_Background_Process::time_exceeded() while running the function to migrate the post meta _customer_user to the field post_author. If the script times out, WP_Background_Process will restart it. --- includes/class-wc-background-updater.php | 4 ++-- includes/wc-update-functions.php | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/includes/class-wc-background-updater.php b/includes/class-wc-background-updater.php index 8fb493a78a3..ba7ebdb1c1e 100644 --- a/includes/class-wc-background-updater.php +++ b/includes/class-wc-background-updater.php @@ -139,7 +139,7 @@ class WC_Background_Updater extends WC_Background_Process { * * @return bool */ - public function is_batch_limit_exceeded() { - return $this->batch_limit_exceeded(); + public function is_memory_exceeded() { + return $this->memory_exceeded(); } } diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 405f3103fd6..fc0857ec16d 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1723,7 +1723,7 @@ function wc_update_350_order_customer_id( $updater = false ) { $wpdb->update( $wpdb->posts, array( 'post_author' => 0 ), array( 'post_type' => 'shop_order_refund' ) ); } else { - // If running the update from the wp-admin, copy data in batches being careful not to use more memory than allowed and respecting PHP time limit. + // If running the update from the wp-admin, copy data in batches being careful not to use more memory than allowed. $admin_orders_sql = ''; // Get the list of orders that we don't want to change as they belong to user ID 1. @@ -1771,8 +1771,8 @@ function wc_update_350_order_customer_id( $updater = false ) { // Update post_author for a batch of orders. foreach ( $orders_meta_data as $order_meta ) { - // Stop update execution and re-enqueue it if near memory and timeout limits. - if ( $updater instanceof WC_Background_Updater && $updater->is_batch_limit_exceeded() ) { + // Stop update execution and re-enqueue it if near memory limit. + if ( $updater instanceof WC_Background_Updater && $updater->is_memory_exceeded() ) { return -1; } @@ -1782,8 +1782,8 @@ function wc_update_350_order_customer_id( $updater = false ) { // Set post_author to 0 instead of 1 on all shop_order_refunds. while ( true ) { - // Stop update execution and re-enqueue it if near memory and timeout limits. - if ( $updater instanceof WC_Background_Updater && $updater->is_batch_limit_exceeded() ) { + // Stop update execution and re-enqueue it if near memory limit. + if ( $updater instanceof WC_Background_Updater && $updater->is_memory_exceeded() ) { return -1; } From 697421a9fae11c317230b3762224bc43947a294c Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Thu, 19 Apr 2018 10:14:02 -0300 Subject: [PATCH 14/40] Cache query to get the list of orders placed by the user ID 1 --- includes/wc-update-functions.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index fc0857ec16d..f2233bc3bc6 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1725,16 +1725,22 @@ function wc_update_350_order_customer_id( $updater = false ) { } else { // If running the update from the wp-admin, copy data in batches being careful not to use more memory than allowed. $admin_orders_sql = ''; + $admin_orders = get_transient( 'wc_update_350_admin_orders' ); - // Get the list of orders that we don't want to change as they belong to user ID 1. - $admin_orders = $wpdb->get_col( - $wpdb->prepare( - "SELECT ID FROM wp_posts p - INNER JOIN wp_postmeta pm ON p.ID = pm.post_id - WHERE post_type IN ({$post_types_placeholders}) AND meta_key = '_customer_user' AND meta_value = 1", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared - $post_types - ) - ); + if ( false === $admin_orders ) { + // Get the list of orders that we don't want to change as they belong to user ID 1. + $admin_orders = $wpdb->get_col( + $wpdb->prepare( + "SELECT ID FROM wp_posts p + INNER JOIN wp_postmeta pm ON p.ID = pm.post_id + WHERE post_type IN ({$post_types_placeholders}) AND meta_key = '_customer_user' AND meta_value = 1", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $post_types + ) + ); + + // Cache the list of orders placed by the user ID 1 as to large stores this query can be slow. + set_transient( 'wc_update_350_admin_orders', $admin_orders, DAY_IN_SECONDS ); + } if ( ! empty( $admin_orders ) ) { $admin_orders_sql .= ' AND ID NOT IN (' . implode( ', ', $admin_orders ) . ') '; From f725dc17aff453f4e21b086393d7a4b154c40530 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 7 May 2018 15:37:25 -0300 Subject: [PATCH 15/40] Set post_author to 0 on all orders that belong to a deleted customer --- includes/wc-user-functions.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php index b6f9e246d92..18b3ac195d8 100644 --- a/includes/wc-user-functions.php +++ b/includes/wc-user-functions.php @@ -494,7 +494,7 @@ function wc_get_customer_order_count( $user_id ) { } /** - * Reset _customer_user on orders when a user is deleted. + * Reset customer ID on orders when a user is deleted. * * @param int $user_id User ID. */ @@ -507,6 +507,21 @@ function wc_reset_order_customer_id_on_deleted_user( $user_id ) { 'meta_value' => $user_id, ) ); // WPCS: slow query ok. + + $post_types = (array) apply_filters( 'woocommerce_reset_order_customer_id_post_types', array( 'shop_order' ) ); + $post_types_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ); + + // Since WC 3.5, the customer ID is stored both in the _customer_user postmeta and in the post_author field. + // In future versions of WC, the plan is to use only post_author and stop using _customer_user, but for now + // we have to update both places. + $wpdb->query( + // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare + $wpdb->prepare( + "UPDATE {$wpdb->posts} SET `post_author` = 0 WHERE post_type IN ({$post_types_placeholders}) AND post_author = {$user_id}", + $post_types + ) + // phpcs:enable + ); } add_action( 'deleted_user', 'wc_reset_order_customer_id_on_deleted_user' ); From 14287bdcf31dff4e767b8ebc955461332929ec4e Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 7 May 2018 17:08:56 -0300 Subject: [PATCH 16/40] Fix PHPCS violations in includes/admin/reports/class-wc-report-customers.php --- .../reports/class-wc-report-customers.php | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/includes/admin/reports/class-wc-report-customers.php b/includes/admin/reports/class-wc-report-customers.php index 60a3f5edb9e..503f470992f 100644 --- a/includes/admin/reports/class-wc-report-customers.php +++ b/includes/admin/reports/class-wc-report-customers.php @@ -1,14 +1,17 @@ sprintf( __( '%s signups in this period', 'woocommerce' ), '' . sizeof( $this->customers ) . '' ), + 'title' => sprintf( __( '%s signups in this period', 'woocommerce' ), '' . count( $this->customers ) . '' ), 'color' => $this->chart_colours['signups'], 'highlight_series' => 2, ); @@ -110,8 +113,8 @@ class WC_Report_Customers extends WC_Admin_Report {
    -
  • -
  • +
  • +