Merge pull request #32857 from woocommerce/cot/backfill

Implement backfill for update posts records from COT
This commit is contained in:
Jorge A. Torres 2022-05-19 15:00:15 +01:00 committed by GitHub
commit d2d8efa7fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 448 additions and 101 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Implement backfill for wp_post and wp_postmeta table for custom tables.

View File

@ -635,7 +635,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
* @throws WC_Data_Exception Exception may be thrown if value is invalid. * @throws WC_Data_Exception Exception may be thrown if value is invalid.
*/ */
public function set_discount_total( $value ) { public function set_discount_total( $value ) {
$this->set_prop( 'discount_total', wc_format_decimal( $value ) ); $this->set_prop( 'discount_total', wc_format_decimal( $value, false, true ) );
} }
/** /**
@ -645,7 +645,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
* @throws WC_Data_Exception Exception may be thrown if value is invalid. * @throws WC_Data_Exception Exception may be thrown if value is invalid.
*/ */
public function set_discount_tax( $value ) { public function set_discount_tax( $value ) {
$this->set_prop( 'discount_tax', wc_format_decimal( $value ) ); $this->set_prop( 'discount_tax', wc_format_decimal( $value, false, true ) );
} }
/** /**
@ -655,7 +655,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
* @throws WC_Data_Exception Exception may be thrown if value is invalid. * @throws WC_Data_Exception Exception may be thrown if value is invalid.
*/ */
public function set_shipping_total( $value ) { public function set_shipping_total( $value ) {
$this->set_prop( 'shipping_total', wc_format_decimal( $value ) ); $this->set_prop( 'shipping_total', wc_format_decimal( $value, false, true ) );
} }
/** /**
@ -665,7 +665,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
* @throws WC_Data_Exception Exception may be thrown if value is invalid. * @throws WC_Data_Exception Exception may be thrown if value is invalid.
*/ */
public function set_shipping_tax( $value ) { public function set_shipping_tax( $value ) {
$this->set_prop( 'shipping_tax', wc_format_decimal( $value ) ); $this->set_prop( 'shipping_tax', wc_format_decimal( $value, false, true ) );
$this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() );
} }
@ -676,7 +676,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
* @throws WC_Data_Exception Exception may be thrown if value is invalid. * @throws WC_Data_Exception Exception may be thrown if value is invalid.
*/ */
public function set_cart_tax( $value ) { public function set_cart_tax( $value ) {
$this->set_prop( 'cart_tax', wc_format_decimal( $value ) ); $this->set_prop( 'cart_tax', wc_format_decimal( $value, false, true ) );
$this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() );
} }

View File

@ -76,6 +76,28 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
'_order_stock_reduced', '_order_stock_reduced',
); );
/**
* Getters for internal key in data stores.
*
* @var string[]
*/
protected $internal_data_store_key_getters = array(
'_download_permissions_granted' => 'download_permissions_granted',
'_recorded_sales' => 'recorded_sales',
'_recorded_coupon_usage_counts' => 'recorded_coupon_usage_counts',
'_order_stock_reduced' => 'stock_reduced',
'_new_order_email_sent' => 'email_sent',
);
/**
* Return internal key getters name.
*
* @return string[]
*/
public function get_internal_data_store_key_getters() {
return $this->internal_data_store_key_getters;
}
/** /**
* Method to create a new order in the database. * Method to create a new order in the database.
* *
@ -297,6 +319,57 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
do_action( 'woocommerce_order_object_updated_props', $order, $updated_props ); do_action( 'woocommerce_order_object_updated_props', $order, $updated_props );
} }
/**
* Given an initialized order object, update the post/postmeta records.
*
* @param WC_Order $order Order object.
*
* @return bool Whether the order was updated.
*/
public function update_order_from_object( $order ) {
if ( ! $order->get_id() ) {
return false;
}
$this->update_order_meta_from_object( $order );
return wp_update_post(
array(
'ID' => $order->get_id(),
'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() ),
'post_status' => $this->get_post_status( $order ),
'post_parent' => $order->get_parent_id(),
'post_excerpt' => $this->get_post_excerpt( $order ),
'post_type' => 'shop_order',
)
);
}
/**
* Helper method to update order metadata from intialized order object.
*
* @param WC_Order $order Order object.
*/
private function update_order_meta_from_object( $order ) {
if ( is_null( $order->get_meta() ) ) {
return;
}
$existing_meta_data = get_post_meta( $order->get_id() );
foreach ( $order->get_meta_data() as $meta_data ) {
if ( isset( $existing_meta_data[ $meta_data->key ] ) ) {
if ( $existing_meta_data[ $meta_data->key ] === $meta_data->value ) {
continue;
}
delete_post_meta( $order->get_id(), $meta_data->key );
unset( $existing_meta_data[ $meta_data->key ] );
}
add_post_meta( $order->get_id(), $meta_data->key, $meta_data->value, false );
}
$this->update_post_meta( $order );
}
/** /**
* Excerpt for post. * Excerpt for post.
* *
@ -630,6 +703,29 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) ); update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
} }
/**
* Whether email have been sent for this order.
*
* @param WC_Order|int $order Order ID or order object.
*
* @return bool Whether email is sent.
*/
public function get_email_sent( $order ) {
$order_id = WC_Order_Factory::get_order_id( $order );
return wc_string_to_bool( get_post_meta( $order_id, '_new_order_email_sent', true ) );
}
/**
* Stores information about whether email was sent.
*
* @param WC_Order|int $order Order ID or order object.
* @param bool $set True or false.
*/
public function set_email_sent( $order, $set ) {
$order_id = WC_Order_Factory::get_order_id( $order );
update_post_meta( $order_id, '_new_order_email_sent', wc_bool_to_string( $set ) );
}
/** /**
* Return array of coupon_code => meta_key for coupon which have usage limit and have tentative keys. * Return array of coupon_code => meta_key for coupon which have usage limit and have tentative keys.
* Pass $coupon_id if key for only one of the coupon is needed. * Pass $coupon_id if key for only one of the coupon is needed.
@ -874,7 +970,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
} else { } else {
update_post_caches( $query->posts ); // We already fetching posts, might as well hydrate some caches. update_post_caches( $query->posts ); // We already fetching posts, might as well hydrate some caches.
$order_ids = wp_list_pluck( $query->posts, 'ID' ); $order_ids = wp_list_pluck( $query->posts, 'ID' );
$orders = $this->compile_orders( $order_ids, $query_vars, $query ); $orders = $this->compile_orders( $order_ids, $query_vars, $query );
} }
if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) { if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) {
@ -942,7 +1038,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
$cache_keys_mapping[ $order_id ] = WC_Cache_Helper::get_cache_prefix( 'orders' ) . 'refunds' . $order_id; $cache_keys_mapping[ $order_id ] = WC_Cache_Helper::get_cache_prefix( 'orders' ) . 'refunds' . $order_id;
} }
$non_cached_ids = array(); $non_cached_ids = array();
$cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' ); $cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' );
foreach ( $order_ids as $order_id ) { foreach ( $order_ids as $order_id ) {
if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) { if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) {
$non_cached_ids[] = $order_id; $non_cached_ids[] = $order_id;
@ -952,11 +1048,11 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
return; return;
} }
$refunds = wc_get_orders( $refunds = wc_get_orders(
array( array(
'type' => 'shop_order_refund', 'type' => 'shop_order_refund',
'post_parent__in' => $non_cached_ids, 'post_parent__in' => $non_cached_ids,
'limit' => - 1, 'limit' => - 1,
) )
); );
$order_refunds = array_reduce( $order_refunds = array_reduce(
@ -1002,13 +1098,13 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
return; return;
} }
} }
$cache_keys = array_map( $cache_keys = array_map(
function ( $order_id ) { function ( $order_id ) {
return 'order-items-' . $order_id; return 'order-items-' . $order_id;
}, },
$order_ids $order_ids
); );
$cache_values = wc_cache_get_multiple( $cache_keys, 'orders' ); $cache_values = wc_cache_get_multiple( $cache_keys, 'orders' );
$non_cached_ids = array(); $non_cached_ids = array();
foreach ( $order_ids as $order_id ) { foreach ( $order_ids as $order_id ) {
if ( false === $cache_values[ 'order-items-' . $order_id ] ) { if ( false === $cache_values[ 'order-items-' . $order_id ] ) {
@ -1019,9 +1115,9 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
return; return;
} }
$non_cached_ids = esc_sql( $non_cached_ids ); $non_cached_ids = esc_sql( $non_cached_ids );
$non_cached_ids_string = implode( ',', $non_cached_ids ); $non_cached_ids_string = implode( ',', $non_cached_ids );
$order_items = $wpdb->get_results( $order_items = $wpdb->get_results(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT order_item_type, order_item_id, order_id, order_item_name FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id in ( $non_cached_ids_string ) ORDER BY order_item_id;" "SELECT order_item_type, order_item_id, order_id, order_item_name FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id in ( $non_cached_ids_string ) ORDER BY order_item_id;"
); );
@ -1068,7 +1164,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
foreach ( $order_ids as $order_id ) { foreach ( $order_ids as $order_id ) {
$cache_keys_mapping[ $order_id ] = WC_Order::generate_meta_cache_key( $order_id, 'orders' ); $cache_keys_mapping[ $order_id ] = WC_Order::generate_meta_cache_key( $order_id, 'orders' );
} }
$cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' ); $cache_values = wc_cache_get_multiple( array_values( $cache_keys_mapping ), 'orders' );
$non_cached_ids = array(); $non_cached_ids = array();
foreach ( $order_ids as $order_id ) { foreach ( $order_ids as $order_id ) {
if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) { if ( false === $cache_values[ $cache_keys_mapping[ $order_id ] ] ) {
@ -1078,8 +1174,8 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
if ( empty( $non_cached_ids ) ) { if ( empty( $non_cached_ids ) ) {
return; return;
} }
$order_ids = esc_sql( $non_cached_ids ); $order_ids = esc_sql( $non_cached_ids );
$order_ids_in = "'" . implode( "', '", $order_ids ) . "'"; $order_ids_in = "'" . implode( "', '", $order_ids ) . "'";
$raw_meta_data_array = $wpdb->get_results( $raw_meta_data_array = $wpdb->get_results(
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT post_id as object_id, meta_id, meta_key, meta_value "SELECT post_id as object_id, meta_id, meta_key, meta_value

View File

@ -115,10 +115,18 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
'type' => 'date_epoch', 'type' => 'date_epoch',
'destination' => 'date_paid_gmt', 'destination' => 'date_paid_gmt',
), ),
'_paid_date' => array( // For compatibility with WC < 2.6.
'type' => 'date',
'destination' => 'date_paid_gmt',
),
'_date_completed' => array( '_date_completed' => array(
'type' => 'date_epoch', 'type' => 'date_epoch',
'destination' => 'date_completed_gmt', 'destination' => 'date_completed_gmt',
), ),
'_completed_date' => array( // For compatibility with WC < 2.6.
'type' => 'date',
'destination' => 'date_completed_gmt',
),
'_order_shipping_tax' => array( '_order_shipping_tax' => array(
'type' => 'decimal', 'type' => 'decimal',
'destination' => 'shipping_tax_amount', 'destination' => 'shipping_tax_amount',

View File

@ -24,32 +24,6 @@ class MigrationHelper {
'bool' => '%d', 'bool' => '%d',
); );
/**
* Get insert clause for appropriate switch.
*
* @param string $switch Name of the switch to use.
*
* @return string Insert clause.
*/
public static function get_insert_switch( string $switch ): string {
switch ( $switch ) {
case 'insert_ignore':
$insert_query = 'INSERT IGNORE';
break;
case 'replace': // delete and then insert.
$insert_query = 'REPLACE';
break;
case 'update':
$insert_query = 'UPDATE';
break;
case 'insert':
default:
$insert_query = 'INSERT';
}
return $insert_query;
}
/** /**
* Helper method to escape backtick in various schema fields. * Helper method to escape backtick in various schema fields.
* *

View File

@ -265,18 +265,30 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
'type' => 'string', 'type' => 'string',
'name' => 'prices_include_tax', 'name' => 'prices_include_tax',
), ),
'coupon_usages_are_counted' => array( 'type' => 'bool' ), 'coupon_usages_are_counted' => array(
'download_permission_granted' => array( 'type' => 'bool' ), 'type' => 'bool',
'name' => 'recorded_coupon_usage_counts',
),
'download_permission_granted' => array(
'type' => 'bool',
'name' => 'download_permissions_granted',
),
'cart_hash' => array( 'cart_hash' => array(
'type' => 'string', 'type' => 'string',
'name' => 'cart_hash', 'name' => 'cart_hash',
), ),
'new_order_email_sent' => array( 'type' => 'string' ), 'new_order_email_sent' => array(
'type' => 'string',
'name' => 'new_order_email_sent',
),
'order_key' => array( 'order_key' => array(
'type' => 'string', 'type' => 'string',
'name' => 'order_key', 'name' => 'order_key',
), ),
'order_stock_reduced' => array( 'type' => 'bool' ), 'order_stock_reduced' => array(
'type' => 'bool',
'name' => 'stock_reduced',
),
'date_paid_gmt' => array( 'date_paid_gmt' => array(
'type' => 'date', 'type' => 'date',
'name' => 'date_paid', 'name' => 'date_paid',
@ -299,7 +311,7 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
), ),
'discount_total_amount' => array( 'discount_total_amount' => array(
'type' => 'decimal', 'type' => 'decimal',
'name' => 'discount_total_amount', 'name' => 'discount_total',
), ),
'recorded_sales' => array( 'type' => 'bool' ), 'recorded_sales' => array( 'type' => 'bool' ),
); );
@ -329,6 +341,162 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
return $this->all_order_column_mapping; return $this->all_order_column_mapping;
} }
/**
* Backfills order details in to WP_Post DB. Uses WC_Order_Data_store_CPT.
*
* @param \WC_Order $order Order object to backfill.
*/
public function backfill_post_record( $order ) {
$cpt_data_store = new \WC_Order_Data_Store_CPT();
$cpt_data_store->update_order_from_object( $order );
foreach ( $cpt_data_store->get_internal_data_store_key_getters() as $key => $getter_name ) {
if (
is_callable( array( $cpt_data_store, "set_$getter_name" ) ) &&
is_callable( array( $this, "get_$getter_name" ) )
) {
call_user_func_array(
array(
$cpt_data_store,
"set_$getter_name",
),
array(
$order,
$this->{"get_$getter_name"}( $order ),
)
);
}
}
}
/**
* Get information about whether permissions are granted yet.
*
* @param \WC_Order $order Order object.
*
* @return bool Whether permissions are granted.
*/
public function get_download_permissions_granted( $order ) {
return wc_string_to_bool( $order->get_meta( '_download_permissions_granted', true ) );
}
/**
* Stores information about whether permissions were generated yet.
*
* @param \WC_Order $order Order ID or order object.
* @param bool $set True or false.
*/
public function set_download_permissions_granted( $order, $set ) {
return $order->update_meta_data( '_download_permissions_granted', wc_bool_to_string( $set ) );
}
/**
* Gets information about whether sales were recorded.
*
* @param \WC_Order $order Order object.
*
* @return bool Whether sales are recorded.
*/
public function get_recorded_sales( $order ) {
return wc_string_to_bool( $order->get_meta( '_recorded_sales', true ) );
}
/**
* Stores information about whether sales were recorded.
*
* @param \WC_Order $order Order object.
* @param bool $set True or false.
*/
public function set_recorded_sales( $order, $set ) {
return $order->update_meta_data( '_recorded_sales', wc_bool_to_string( $set ) );
}
/**
* Gets information about whether coupon counts were updated.
*
* @param \WC_Order $order Order object.
*
* @return bool Whether coupon counts were updated.
*/
public function get_recorded_coupon_usage_counts( $order ) {
return wc_string_to_bool( $order->get_meta( '_recorded_coupon_usage_counts', true ) );
}
/**
* Stores information about whether coupon counts were updated.
*
* @param \WC_Order $order Order object.
* @param bool $set True or false.
*/
public function set_recorded_coupon_usage_counts( $order, $set ) {
return $order->update_meta_data( '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
}
/**
* Whether email have been sent for this order.
*
* @param \WC_Order|int $order Order object.
*
* @return bool Whether email is sent.
*/
public function get_email_sent( $order ) {
return wc_string_to_bool( $order->get_meta( '_new_order_email_sent', true ) );
}
/**
* Stores information about whether email was sent.
*
* @param \WC_Order $order Order object.
*
* @param bool $set True or false.
*/
public function set_email_sent( $order, $set ) {
return $order->update_meta_data( '_new_order_email_sent', wc_bool_to_string( $set ) );
}
/**
* Helper setter for email_sent.
*
* @param \WC_Order $order Order object.
*
* @return bool Whether email was sent.
*/
private function get_new_order_email_sent( $order ) {
return $this->get_email_sent( $order );
}
/**
* Helper setter for new order email sent.
*
* @param \WC_Order $order Order object.
* @param bool $set True or false.
*
* @return bool Whether email was sent.
*/
private function set_new_order_email_sent( $order, $set ) {
return $this->set_email_sent( $order, $set );
}
/**
* Gets information about whether stock was reduced.
*
* @param \WC_Order $order Order object.
*
* @return bool Whether stock was reduced.
*/
public function get_stock_reduced( $order ) {
return wc_string_to_bool( $order->get_meta( '_order_stock_reduced', true ) );
}
/**
* Stores information about whether stock was reduced.
*
* @param \WC_Order $order Order ID or order object.
* @param bool $set True or false.
*/
public function set_stock_reduced( $order, $set ) {
return $order->update_meta_data( '_order_stock_reduced', wc_string_to_bool( $set ) );
}
//phpcs:disable Squiz.Commenting, Generic.Commenting //phpcs:disable Squiz.Commenting, Generic.Commenting
// TODO: Add methods for other table names as appropriate. // TODO: Add methods for other table names as appropriate.
@ -372,33 +540,6 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
return array(); return array();
} }
public function get_download_permissions_granted( $order ) {
// TODO: Implement get_download_permissions_granted() method.
false;
}
public function set_download_permissions_granted( $order, $set ) {
// TODO: Implement set_download_permissions_granted() method.
}
public function get_recorded_sales( $order ) {
// TODO: Implement get_recorded_sales() method.
return false;
}
public function set_recorded_sales( $order, $set ) {
// TODO: Implement set_recorded_sales() method.
}
public function get_recorded_coupon_usage_counts( $order ) {
// TODO: Implement get_recorded_coupon_usage_counts() method.
return false;
}
public function set_recorded_coupon_usage_counts( $order, $set ) {
// TODO: Implement set_recorded_coupon_usage_counts() method.
}
public function get_order_type( $order_id ) { public function get_order_type( $order_id ) {
// TODO: Implement get_order_type() method. // TODO: Implement get_order_type() method.
return 'shop_order'; return 'shop_order';
@ -418,6 +559,7 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
if ( ! $order->get_id() ) { if ( ! $order->get_id() ) {
throw new \Exception( __( 'ID must be set for an order to be read', 'woocommerce' ) ); throw new \Exception( __( 'ID must be set for an order to be read', 'woocommerce' ) );
} }
$order->read_meta_data();
$order_data = $this->get_order_data_for_id( $order->get_id() ); $order_data = $this->get_order_data_for_id( $order->get_id() );
foreach ( $this->get_all_order_column_mappings() as $table_name => $column_mapping ) { foreach ( $this->get_all_order_column_mappings() as $table_name => $column_mapping ) {
foreach ( $column_mapping as $column_name => $prop_details ) { foreach ( $column_mapping as $column_name => $prop_details ) {
@ -427,6 +569,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
$prop_setter_function_name = "set_{$prop_details['name']}"; $prop_setter_function_name = "set_{$prop_details['name']}";
if ( is_callable( array( $order, $prop_setter_function_name ) ) ) { if ( is_callable( array( $order, $prop_setter_function_name ) ) ) {
$order->{$prop_setter_function_name}( $order_data->{$prop_details['name']} ); $order->{$prop_setter_function_name}( $order_data->{$prop_details['name']} );
} elseif ( is_callable( array( $this, $prop_setter_function_name ) ) ) {
$this->{$prop_setter_function_name}( $order, $order_data->{$prop_details['name']} );
} }
} }
} }
@ -434,6 +578,33 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
$order->set_object_read(); $order->set_object_read();
} }
/**
* Read metadata directly from database.
*
* @param \WC_Order $order Order object.
*
* @return array Metadata array.
*/
public function read_meta( &$order ) {
global $wpdb;
$meta_table = $this::get_meta_table_name();
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $meta_table is hardcoded.
$raw_meta_data = $wpdb->get_results(
$wpdb->prepare(
"
SELECT id as meta_id, meta_key, meta_value
FROM $meta_table
WHERE order_id = %d
ORDER BY meta_id;
",
$order->get_id()
)
);
// phpcs:enable
return $this->filter_raw_meta_data( $order, $raw_meta_data );
}
/** /**
* Return order data for a single order ID. * Return order data for a single order ID.
* *
@ -537,6 +708,7 @@ LEFT JOIN {$operational_data_clauses['join']}
"{$clauses['join']} AND $address_table_alias.address_type = %s", "{$clauses['join']} AND $address_table_alias.address_type = %s",
$address_type $address_type
); );
// phpcs:enable // phpcs:enable
return array( return array(
'select' => $clauses['select'], 'select' => $clauses['select'],
@ -603,6 +775,7 @@ LEFT JOIN {$operational_data_clauses['join']}
//phpcs:disable Squiz.Commenting, Generic.Commenting //phpcs:disable Squiz.Commenting, Generic.Commenting
/** /**
* @param \WC_Order $order * @param \WC_Order $order
*/ */
@ -630,14 +803,6 @@ LEFT JOIN {$operational_data_clauses['join']}
throw new \Exception( 'Unimplemented' ); throw new \Exception( 'Unimplemented' );
} }
public function get_stock_reduced( $order ) {
return false;
}
public function set_stock_reduced( $order, $set ) {
throw new \Exception( 'Unimplemented' );
}
public function query( $query_vars ) { public function query( $query_vars ) {
return array(); return array();
} }

View File

@ -1,4 +1,5 @@
<?php // phpcs:ignore <?php // phpcs:ignore
/** /**
* Orders helper. * Orders helper.
* *
@ -12,6 +13,8 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController; use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer; use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use WC_Mock_Payment_Gateway; use WC_Mock_Payment_Gateway;
use WC_Order;
use WC_Product;
use \WC_Tax; use \WC_Tax;
use \WC_Shipping_Rate; use \WC_Shipping_Rate;
use \WC_Order_Item_Shipping; use \WC_Order_Item_Shipping;
@ -47,13 +50,13 @@ class OrderHelper {
/** /**
* Create a order. * Create a order.
* *
* @since 2.4
* @version 3.0 New parameter $product.
*
* @param int $customer_id Customer ID. * @param int $customer_id Customer ID.
* @param WC_Product $product Product object. * @param WC_Product $product Product object.
* *
* @return WC_Order WC_Order object. * @return WC_Order WC_Order object.
* @version 3.0 New parameter $product.
*
* @since 2.4
*/ */
public static function create_order( $customer_id = 1, $product = null ) { public static function create_order( $customer_id = 1, $product = null ) {
@ -132,6 +135,20 @@ class OrderHelper {
return $order; return $order;
} }
/**
* Helper method to drop custom tables if present.
*/
public static function delete_order_custom_tables() {
$order_table_controller = wc_get_container()
->get( CustomOrdersTableController::class );
$order_table_controller->show_feature();
$synchronizer = wc_get_container()
->get( DataSynchronizer::class );
if ( $synchronizer->check_orders_table_exists() ) {
$synchronizer->delete_database_tables();
}
}
/** /**
* Helper method to create custom tables if not present. * Helper method to create custom tables if not present.
*/ */
@ -208,8 +225,8 @@ class OrderHelper {
$order->get_data_store()->set_download_permissions_granted( $order, true ); $order->get_data_store()->set_download_permissions_granted( $order, true );
$order->get_data_store()->set_recorded_sales( $order, true ); $order->get_data_store()->set_recorded_sales( $order, true );
$order->set_cart_hash( '1234' ); $order->set_cart_hash( '1234' );
$order->update_meta_data( '_new_order_email_sent', 'true' ); $order->get_data_store()->set_email_sent( $order, true );
$order->update_meta_data( '_order_stock_reduced', 'true' ); $order->get_data_store()->stock_reduced( $order, true );
$order->set_date_paid( time() ); $order->set_date_paid( time() );
$order->set_date_completed( time() ); $order->set_date_completed( time() );
$order->calculate_shipping(); $order->calculate_shipping();

View File

@ -1,6 +1,7 @@
<?php <?php
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController; use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore; use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper; use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
@ -34,13 +35,21 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
// Remove the Test Suites use of temporary tables https://wordpress.stackexchange.com/a/220308. // Remove the Test Suites use of temporary tables https://wordpress.stackexchange.com/a/220308.
remove_filter( 'query', array( $this, '_create_temporary_tables' ) ); remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) ); remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
OrderHelper::delete_order_custom_tables(); // We need this since non-temporary tables won't drop automatically.
OrderHelper::create_order_custom_table_if_not_exist(); OrderHelper::create_order_custom_table_if_not_exist();
$this->sut = wc_get_container()->get( OrdersTableDataStore::class ); $this->sut = wc_get_container()->get( OrdersTableDataStore::class );
$this->migrator = wc_get_container()->get( PostsToOrdersMigrationController::class ); $this->migrator = wc_get_container()->get( PostsToOrdersMigrationController::class );
$this->cpt_data_store = new WC_Order_Data_Store_CPT(); $this->cpt_data_store = new WC_Order_Data_Store_CPT();
}
/**
* Destroys system under test.
*/
public function tearDown(): void {
// Add back removed filter. // Add back removed filter.
add_filter( 'query', array( $this, '_create_temporary_tables' ) ); add_filter( 'query', array( $this, '_create_temporary_tables' ) );
add_filter( 'query', array( $this, '_drop_temporary_tables' ) ); add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
parent::tearDown();
} }
/** /**
@ -50,26 +59,100 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$post_order_id = OrderHelper::create_complex_wp_post_order(); $post_order_id = OrderHelper::create_complex_wp_post_order();
$this->migrator->migrate_orders( array( $post_order_id ) ); $this->migrator->migrate_orders( array( $post_order_id ) );
wp_cache_flush();
$cot_order = new WC_Order(); $cot_order = new WC_Order();
$cot_order->set_id( $post_order_id ); $cot_order->set_id( $post_order_id );
$this->switch_data_store( $cot_order, $this->sut );
$this->sut->read( $cot_order ); $this->sut->read( $cot_order );
wp_cache_flush();
$post_order = new WC_Order(); $post_order = new WC_Order();
$post_order->set_id( $post_order_id ); $post_order->set_id( $post_order_id );
$this->switch_data_store( $post_order, $this->cpt_data_store );
$this->cpt_data_store->read( $post_order ); $this->cpt_data_store->read( $post_order );
$post_order_data = $post_order->get_data(); $this->assertEquals( $post_order->get_base_data(), $cot_order->get_base_data() );
$string_to_num_keys = array( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'cart_tax' ); $post_order_meta_keys = wp_list_pluck( $post_order->get_meta_data(), 'key' );
array_walk( foreach ( $post_order_meta_keys as $meta_key ) {
$post_order_data, $this->assertEquals( $post_order->get_meta( $meta_key ), $cot_order->get_meta( $meta_key ) );
function ( &$data, $key ) use ( $string_to_num_keys ) { }
if ( in_array( $key, $string_to_num_keys, true ) ) {
$data = (float) $data;
}
}
);
$this->assertEquals( $post_order_data, $cot_order->get_data() );
} }
/**
* Test whether backfill_post_record works as expected.
*/
public function test_backfill_post_record() {
$post_order_id = OrderHelper::create_complex_wp_post_order();
$this->migrator->migrate_orders( array( $post_order_id ) );
$post_data = get_post( $post_order_id, ARRAY_A );
$post_meta_data = get_post_meta( $post_order_id );
// TODO: Remove `_recorded_sales` from exempted keys after https://github.com/woocommerce/woocommerce/issues/32843.
$exempted_keys = array( 'post_modified', 'post_modified_gmt', '_recorded_sales' );
$convert_to_float_keys = array( '_cart_discount_tax', '_order_shipping', '_order_shipping_tax', '_order_tax', '_cart_discount', 'cart_tax' );
$exempted_keys = array_flip( array_merge( $exempted_keys, $convert_to_float_keys ) );
$post_data_float = array_intersect_key( $post_data, array_flip( $convert_to_float_keys ) );
$post_meta_data_float = array_intersect_key( $post_meta_data, array_flip( $convert_to_float_keys ) );
$post_data = array_diff_key( $post_data, $exempted_keys );
$post_meta_data = array_diff_key( $post_meta_data, $exempted_keys );
// Let's update post data.
wp_update_post(
array(
'ID' => $post_order_id,
'post_status' => 'migration_pending',
'post_type' => DataSynchronizer::PLACEHOLDER_ORDER_POST_TYPE,
'ping_status' => 'closed',
'post_parent' => 0,
'menu_order' => 0,
'post_date' => '',
'post_date_gmt' => '',
)
);
$this->delete_all_meta_for_post( $post_order_id );
$this->assertEquals( 'migration_pending', get_post_status( $post_order_id ) ); // assert post was updated.
$this->assertEquals( array(), get_post_meta( $post_order_id ) ); // assert postmeta was deleted.
$cot_order = new WC_Order();
$cot_order->set_id( $post_order_id );
$this->switch_data_store( $cot_order, $this->sut );
$this->sut->read( $cot_order );
$this->sut->backfill_post_record( $cot_order );
$this->assertEquals( $post_data, array_diff_key( get_post( $post_order_id, ARRAY_A ), $exempted_keys ) );
$this->assertEquals( $post_meta_data, array_diff_key( get_post_meta( $post_order_id ), $exempted_keys ) );
foreach ( $post_data_float as $float_key => $value ) {
$this->assertEquals( (float) get_post( $post_order_id, ARRAY_A )[ $float_key ], (float) $value, "Value for $float_key does not match." );
}
foreach ( $post_meta_data_float as $float_key => $value ) {
$this->assertEquals( (float) get_post_meta( $post_order_id )[ $float_key ], (float) $value, "Value for $float_key does not match." );
}
}
/**
* Helper function to delete all meta for post.
*
* @param int $post_id Post ID to delete data for.
*/
private function delete_all_meta_for_post( $post_id ) {
global $wpdb;
$wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) );
}
/**
* Helper method to allow switching data stores.
*
* @param WC_Order $order Order object.
* @param WC_Data_Store $data_store Data store object to switch order to.
*/
private function switch_data_store( $order, $data_store ) {
$update_data_store_func = function ( $data_store ) {
$this->data_store = $data_store;
};
$update_data_store_func->call( $order, $data_store );
}
} }