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 1b562292c2a..f9babc8825b 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
@@ -772,15 +772,22 @@ 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.
+
+ // 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
+ $query_vars['meta_query'] = array(
+ array(
+ 'key' => '_customer_user',
+ 'value' => $customer_id,
+ 'compare' => '=',
+ ),
+ );
+ // @codingStandardsIgnoreEnd
+ }
}
// Sorting.
diff --git a/includes/admin/reports/class-wc-report-customers.php b/includes/admin/reports/class-wc-report-customers.php
index 503f470992f..326ad5334f5 100644
--- a/includes/admin/reports/class-wc-report-customers.php
+++ b/includes/admin/reports/class-wc-report-customers.php
@@ -69,46 +69,56 @@ class WC_Report_Customers extends WC_Admin_Report {
* Output customers vs guests chart.
*/
public function customers_vs_guests() {
-
- $customer_order_totals = $this->get_order_report_data(
- array(
- 'data' => array(
- 'ID' => array(
- 'type' => 'post_data',
- 'function' => 'COUNT',
- 'name' => 'total_orders',
- ),
+ $customer_args = array(
+ 'data' => array(
+ 'ID' => array(
+ 'type' => 'post_data',
+ 'function' => 'COUNT',
+ 'name' => 'total_orders',
),
- 'where_meta' => array(
- array(
- 'meta_key' => '_customer_user',
- 'meta_value' => '0',
- 'operator' => '>',
- ),
- ),
- 'filter_range' => true,
- )
+ ),
+ 'filter_range' => true,
);
+ $guest_args = $customer_args;
- $guest_order_totals = $this->get_order_report_data(
- array(
- 'data' => array(
- 'ID' => array(
- 'type' => 'post_data',
- 'function' => 'COUNT',
- 'name' => 'total_orders',
- ),
+ // 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_args['where'] = array(
+ array(
+ 'key' => 'post_author',
+ 'value' => '0',
+ 'operator' => '>',
),
- 'where_meta' => array(
- array(
- 'meta_key' => '_customer_user',
- 'meta_value' => '0',
- 'operator' => '=',
- ),
+ );
+
+ $guest_args['where'] = array(
+ array(
+ 'key' => 'post_author',
+ 'value' => '0',
+ 'operator' => '=',
),
- 'filter_range' => true,
- )
- );
+ );
+ } else {
+ $customer_args['where_meta'] = array(
+ array(
+ 'meta_key' => '_customer_user',
+ 'meta_value' => '0',
+ 'operator' => '>',
+ ),
+ );
+
+ $guest_args['where_meta'] = array(
+ array(
+ 'meta_key' => '_customer_user',
+ 'meta_value' => '0',
+ 'operator' => '=',
+ ),
+ );
+ }
+
+ $customer_order_totals = $this->get_order_report_data( $customer_args );
+ $guest_order_totals = $this->get_order_report_data( $guest_args );
+
?>
@@ -246,61 +256,63 @@ class WC_Report_Customers extends WC_Admin_Report {
public function get_main_chart() {
global $wp_locale;
- $customer_orders = $this->get_order_report_data(
- array(
- 'data' => array(
- 'ID' => array(
- 'type' => 'post_data',
- 'function' => 'COUNT',
- 'name' => 'total_orders',
- ),
- 'post_date' => array(
- 'type' => 'post_data',
- 'function' => '',
- 'name' => 'post_date',
- ),
+ $customer_args = array(
+ 'data' => array(
+ 'ID' => array(
+ 'type' => 'post_data',
+ 'function' => 'COUNT',
+ 'name' => 'total_orders',
),
- 'where_meta' => array(
- array(
- 'meta_key' => '_customer_user',
- 'meta_value' => '0',
- 'operator' => '>',
- ),
+ 'post_date' => array(
+ 'type' => 'post_data',
+ 'function' => '',
+ 'name' => 'post_date',
),
- 'group_by' => $this->group_by_query,
- 'order_by' => 'post_date ASC',
- 'query_type' => 'get_results',
- 'filter_range' => true,
- )
+ ),
+ 'group_by' => $this->group_by_query,
+ 'order_by' => 'post_date ASC',
+ 'query_type' => 'get_results',
+ 'filter_range' => true,
);
+ $guest_args = $customer_args;
- $guest_orders = $this->get_order_report_data(
- array(
- 'data' => array(
- 'ID' => array(
- 'type' => 'post_data',
- 'function' => 'COUNT',
- 'name' => 'total_orders',
- ),
- 'post_date' => array(
- 'type' => 'post_data',
- 'function' => '',
- 'name' => 'post_date',
- ),
+ // 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_args['where'] = array(
+ array(
+ 'key' => 'post_author',
+ 'value' => '0',
+ 'operator' => '>',
),
- 'where_meta' => array(
- array(
- 'meta_key' => '_customer_user',
- 'meta_value' => '0',
- 'operator' => '=',
- ),
+ );
+
+ $guest_args['where'] = array(
+ array(
+ 'key' => 'post_author',
+ 'value' => '0',
+ 'operator' => '=',
),
- 'group_by' => $this->group_by_query,
- 'order_by' => 'post_date ASC',
- 'query_type' => 'get_results',
- 'filter_range' => true,
- )
- );
+ );
+ } else {
+ $customer_args['where_meta'] = array(
+ array(
+ 'meta_key' => '_customer_user',
+ 'meta_value' => '0',
+ 'operator' => '>',
+ ),
+ );
+
+ $guest_args['where_meta'] = array(
+ array(
+ 'meta_key' => '_customer_user',
+ 'meta_value' => '0',
+ 'operator' => '=',
+ ),
+ );
+ }
+
+ $customer_orders = $this->get_order_report_data( $customer_args );
+ $guest_orders = $this->get_order_report_data( $guest_args );
$signups = $this->prepare_chart_data( $this->customers, 'user_registered', '', $this->chart_interval, $this->start_date, $this->chart_groupby );
$customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php
index 02957242ac0..c9121bb4465 100644
--- a/includes/api/class-wc-rest-orders-controller.php
+++ b/includes/api/class-wc-rest-orders-controller.php
@@ -365,15 +365,20 @@ class WC_REST_Orders_Controller extends WC_REST_Legacy_Orders_Controller {
}
if ( isset( $request['customer'] ) ) {
- if ( ! empty( $args['meta_query'] ) ) {
- $args['meta_query'] = array(); // WPCS: slow query ok.
- }
+ // 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', '>=' ) ) {
+ $args['author'] = $request['customer'];
+ } else {
+ if ( ! empty( $args['meta_query'] ) ) {
+ $args['meta_query'] = array(); // WPCS: slow query ok.
+ }
- $args['meta_query'][] = array(
- 'key' => '_customer_user',
- 'value' => $request['customer'],
- 'type' => 'NUMERIC',
- );
+ $args['meta_query'][] = array(
+ 'key' => '_customer_user',
+ 'value' => $request['customer'],
+ 'type' => 'NUMERIC',
+ );
+ }
}
// Search by product.
diff --git a/includes/api/v1/class-wc-rest-orders-controller.php b/includes/api/v1/class-wc-rest-orders-controller.php
index 804e3fb571e..f617192c0bb 100644
--- a/includes/api/v1/class-wc-rest-orders-controller.php
+++ b/includes/api/v1/class-wc-rest-orders-controller.php
@@ -404,15 +404,20 @@ class WC_REST_Orders_V1_Controller extends WC_REST_Posts_Controller {
}
if ( isset( $request['customer'] ) ) {
- if ( ! empty( $args['meta_query'] ) ) {
- $args['meta_query'] = array();
- }
+ // 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', '>=' ) ) {
+ $args['author'] = $request['customer'];
+ } else {
+ if ( ! empty( $args['meta_query'] ) ) {
+ $args['meta_query'] = array(); // WPCS: slow query ok.
+ }
- $args['meta_query'][] = array(
- 'key' => '_customer_user',
- 'value' => $request['customer'],
- 'type' => 'NUMERIC',
- );
+ $args['meta_query'][] = array(
+ 'key' => '_customer_user',
+ 'value' => $request['customer'],
+ 'type' => 'NUMERIC',
+ );
+ }
}
// Search by product.
diff --git a/includes/class-wc-background-updater.php b/includes/class-wc-background-updater.php
index 3f80666a767..1c28f7f26da 100644
--- a/includes/class-wc-background-updater.php
+++ b/includes/class-wc-background-updater.php
@@ -132,4 +132,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_memory_exceeded() {
+ return $this->memory_exceeded();
+ }
}
diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php
index b0bcb358020..e07100c8262 100644
--- a/includes/class-wc-install.php
+++ b/includes/class-wc-install.php
@@ -109,6 +109,10 @@ class WC_Install {
'wc_update_343_cleanup_foreign_keys',
'wc_update_343_db_version',
),
+ '3.5.0' => array(
+ 'wc_update_350_order_customer_id',
+ 'wc_update_350_db_version',
+ ),
);
/**
diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php
index a98d4076c85..9f7e1380f85 100644
--- a/includes/class-woocommerce.php
+++ b/includes/class-woocommerce.php
@@ -20,7 +20,7 @@ final class WooCommerce {
*
* @var string
*/
- public $version = '3.4.0';
+ public $version = '3.5.0';
/**
* The single instance of the class.
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 73ff2844cb0..0d6208887da 100644
--- a/includes/data-stores/abstract-wc-order-data-store-cpt.php
+++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php
@@ -67,7 +67,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() : 0,
'post_title' => $this->get_post_title(),
'post_password' => uniqid( 'order_' ),
'post_parent' => $order->get_parent_id( 'edit' ),
@@ -139,7 +139,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() ),
@@ -148,6 +148,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() : 0,
);
/**
@@ -165,12 +166,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-customer-data-store.php b/includes/data-stores/class-wc-customer-data-store.php
index d907e61893c..8ad38340b3f 100644
--- a/includes/data-stores/class-wc-customer-data-store.php
+++ b/includes/data-stores/class-wc-customer-data-store.php
@@ -325,18 +325,27 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
public function get_last_order( &$customer ) {
global $wpdb;
- $last_order = $wpdb->get_var(
- // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
- "SELECT posts.ID
+ // 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 = "SELECT ID
+ FROM $wpdb->posts
+ WHERE post_author = '" . esc_sql( $customer->get_id() ) . "'
+ AND post_type = 'shop_order'
+ AND post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
+ ORDER BY ID DESC";
+ } else {
+ $query = "SELECT posts.ID
FROM $wpdb->posts AS posts
LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
WHERE meta.meta_key = '_customer_user'
AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "'
AND posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
- ORDER BY posts.ID DESC"
- // phpcs:enable
- );
+ ORDER BY posts.ID DESC";
+ }
+
+ // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
+ $last_order = $wpdb->get_var( $query );
if ( ! $last_order ) {
return false;
@@ -358,17 +367,25 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
if ( '' === $count ) {
global $wpdb;
- $count = $wpdb->get_var(
- // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
- "SELECT COUNT(*)
+ // 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 = "SELECT COUNT(*)
+ FROM $wpdb->posts
+ WHERE post_type = 'shop_order'
+ AND post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
+ AND post_author = " . esc_sql( $customer->get_id() );
+ } else {
+ $query = "SELECT COUNT(*)
FROM $wpdb->posts as posts
LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
WHERE meta.meta_key = '_customer_user'
AND posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
- AND meta_value = '" . esc_sql( $customer->get_id() ) . "'"
- // phpcs:enable
- );
+ AND meta_value = '" . esc_sql( $customer->get_id() ) . "'";
+ }
+
+ // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
update_user_meta( $customer->get_id(), '_order_count', $count );
}
@@ -393,11 +410,18 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
global $wpdb;
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
- $spent = $wpdb->get_var(
- // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
- apply_filters(
- 'woocommerce_customer_get_total_spent_query',
- "SELECT SUM(meta2.meta_value)
+
+ // 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 = "SELECT SUM(meta.meta_value)
+ FROM $wpdb->posts as posts
+ LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
+ WHERE posts.post_author = '" . esc_sql( $customer->get_id() ) . "'
+ AND posts.post_type = 'shop_order'
+ AND posts.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
+ AND meta.meta_key = '_order_total'";
+ } else {
+ $query = "SELECT SUM(meta2.meta_value)
FROM $wpdb->posts as posts
LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id
@@ -405,11 +429,11 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "'
AND posts.post_type = 'shop_order'
AND posts.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
- AND meta2.meta_key = '_order_total'",
- $customer
- )
- // phpcs:enable
- );
+ AND meta2.meta_key = '_order_total'";
+ }
+
+ // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
+ $spent = $wpdb->get_var( apply_filters( 'woocommerce_customer_get_total_spent_query', $query, $customer ) );
if ( ! $spent ) {
$spent = 0;
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 2cc5b7e1fcd..316aa4b8373 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 );
}
+ // 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 );
+ }
+
$order->set_props(
array(
'order_key' => get_post_meta( $id, '_order_key', true ),
- 'customer_id' => get_post_meta( $id, '_customer_user', true ),
+ '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 ),
@@ -258,10 +265,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_id', $updated_props, true ) || in_array( 'billing_email', $updated_props, true ) ) {
- $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 );
}
// Mark user account as active.
@@ -645,6 +651,11 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
'page' => 'paged',
);
+ // 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', '>=' ) ) {
+ $key_mapping['customer_id'] = 'author';
+ }
+
foreach ( $key_mapping as $query_key => $db_key ) {
if ( isset( $query_vars[ $query_key ] ) ) {
$query_vars[ $db_key ] = $query_vars[ $query_key ];
diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php
index ba56e596ab2..c564831bbdf 100644
--- a/includes/wc-update-functions.php
+++ b/includes/wc-update-functions.php
@@ -1835,3 +1835,130 @@ function wc_update_343_cleanup_foreign_keys() {
function wc_update_343_db_version() {
WC_Install::update_db_version( '3.4.3' );
}
+
+/**
+ * Copy order customer_id from post meta to post_author and set post_author to 0 for refunds.
+ *
+ * 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.
+ * @return true|void Return true if near memory limit and needs to restart. Return void if update completed.
+ */
+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' ) );
+
+ 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))'
+ );
+
+ $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.
+ $admin_orders_sql = '';
+ $admin_orders = get_transient( 'wc_update_350_admin_orders' );
+
+ 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 ) . ') ';
+ }
+
+ // 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 limit.
+ if ( $updater instanceof WC_Background_Updater && $updater->is_memory_exceeded() ) {
+ return true;
+ }
+
+ $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 limit.
+ if ( $updater instanceof WC_Background_Updater && $updater->is_memory_exceeded() ) {
+ return true;
+ }
+
+ $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();
+}
+
+/**
+ * Update DB Version.
+ */
+function wc_update_350_db_version() {
+ WC_Install::update_db_version( '3.5.0' );
+}
diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php
index a302ce008bf..b8f62fa7bc8 100644
--- a/includes/wc-user-functions.php
+++ b/includes/wc-user-functions.php
@@ -219,11 +219,15 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) {
$result = get_transient( $transient_name );
if ( false === $result ) {
- $customer_data = array( $user_id );
+ $customer_data = array();
if ( $user_id ) {
$user = get_user_by( 'id', $user_id );
+ if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '<' ) ) {
+ $customer_data[] = $user_id;
+ }
+
if ( isset( $user->user_email ) ) {
$customer_data[] = $user->user_email;
}
@@ -240,19 +244,31 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) {
return false;
}
- $result = $wpdb->get_col(
- "
- SELECT im.meta_value FROM {$wpdb->posts} AS p
- INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
- INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
- INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
- WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
- AND pm.meta_key IN ( '_billing_email', '_customer_user' )
- AND im.meta_key IN ( '_product_id', '_variation_id' )
- AND im.meta_value != 0
- AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )
- "
- ); // WPCS: unprepared SQL ok.
+ if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) && $user_id ) {
+ // Since WC 3.5 wp_posts.post_author is used to store the ID of the customer who placed an order.
+ $query = "SELECT im.meta_value FROM {$wpdb->posts} AS p
+ INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
+ WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
+ AND p.post_author = {$user_id}
+ AND pm.meta_key = '_billing_email'
+ AND im.meta_key IN ( '_product_id', '_variation_id' )
+ AND im.meta_value != 0
+ AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )";
+ } else {
+ $query = "SELECT im.meta_value FROM {$wpdb->posts} AS p
+ INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
+ WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
+ AND pm.meta_key IN ( '_billing_email', '_customer_user' )
+ AND im.meta_key IN ( '_product_id', '_variation_id' )
+ AND im.meta_value != 0
+ AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )";
+ }
+
+ $result = $wpdb->get_col( $query ); // WPCS: unprepared SQL ok.
$result = array_map( 'absint', $result );
set_transient( $transient_name, $result, DAY_IN_SECONDS * 30 );
@@ -495,7 +511,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.
*/
@@ -508,6 +524,22 @@ 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' ) );
+ $query_args = array_merge( $post_types, array( $user_id ) );
+
+ // 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 = %d",
+ $query_args
+ )
+ // phpcs:enable
+ );
}
add_action( 'deleted_user', 'wc_reset_order_customer_id_on_deleted_user' );