Synchronize order meta data (between HPOS and CPT stores) (#36593)

This commit is contained in:
Jorge A. Torres 2023-03-23 10:03:44 -05:00 committed by GitHub
commit 627c2bce01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 20 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
When order meta data is saved via HPOS, it should be backfilled to the CPT data store.

View File

@ -613,14 +613,28 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
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 ) {
unset( $existing_meta_data[ $meta_data->key ] );
continue;
}
delete_post_meta( $order->get_id(), $meta_data->key );
unset( $existing_meta_data[ $meta_data->key ] );
delete_post_meta( $order->get_id(), $meta_data->key );
}
add_post_meta( $order->get_id(), $meta_data->key, $meta_data->value, false );
}
// Find remaining meta that was deleted from the order but still present in the associated post.
// Post meta corresponding to order props is excluded (as it shouldn't be deleted).
$keys_to_delete = array_diff(
array_keys( $existing_meta_data ),
$this->internal_meta_keys,
array_keys( $this->get_internal_data_store_key_getters() )
);
foreach ( $keys_to_delete as $meta_key ) {
delete_post_meta( $order->get_id(), $meta_key );
}
$this->update_post_meta( $order );
}
}

View File

@ -78,6 +78,8 @@ abstract class CustomMetaDataStore {
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing at least ->id).
*
* @return bool
*/
public function delete_meta( &$object, $meta ) {
global $wpdb;
@ -127,6 +129,8 @@ abstract class CustomMetaDataStore {
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->id, ->key and ->value).
*
* @return bool
*/
public function update_meta( &$object, $meta ) {
global $wpdb;

View File

@ -11,6 +11,7 @@ use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Utilities\ArrayUtil;
use Exception;
use WC_Abstract_Order;
use WC_Data;
use WC_Order;
@ -1963,21 +1964,6 @@ FROM $order_meta_table
$order->delete_meta_data( '_wp_trash_meta_comments_status' );
$order->save_meta_data();
$data_synchronizer = wc_get_container()->get( DataSynchronizer::class );
if ( $data_synchronizer->data_sync_is_enabled() ) {
// The previous $order->save() will have forced a sync to the posts table,
// this implies that the post status is not "trash" anymore, and thus
// wp_untrash_post would do nothing.
wp_update_post(
array(
'ID' => $id,
'post_status' => 'trash',
)
);
wp_untrash_post( $id );
}
return true;
}
@ -2457,9 +2443,17 @@ CREATE TABLE $meta_table (
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing at least ->id).
*
* @return bool
*/
public function delete_meta( &$object, $meta ) {
return $this->data_store_meta->delete_meta( $object, $meta );
$delete_meta = $this->data_store_meta->delete_meta( $object, $meta );
if ( $object instanceof WC_Abstract_Order ) {
$this->maybe_backfill_post_record( $object );
}
return $delete_meta;
}
/**
@ -2467,10 +2461,17 @@ CREATE TABLE $meta_table (
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->key and ->value).
* @return int meta ID
*
* @return int|bool meta ID or false on failure
*/
public function add_meta( &$object, $meta ) {
return $this->data_store_meta->add_meta( $object, $meta );
$add_meta = $this->data_store_meta->add_meta( $object, $meta );
if ( $object instanceof WC_Abstract_Order ) {
$this->maybe_backfill_post_record( $object );
}
return $add_meta;
}
/**
@ -2478,8 +2479,16 @@ CREATE TABLE $meta_table (
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->id, ->key and ->value).
*
* @return bool
*/
public function update_meta( &$object, $meta ) {
return $this->data_store_meta->update_meta( $object, $meta );
$update_meta = $this->data_store_meta->update_meta( $object, $meta );
if ( $object instanceof WC_Abstract_Order ) {
$this->maybe_backfill_post_record( $object );
}
return $update_meta;
}
}

View File

@ -223,4 +223,74 @@ class DataSynchronizerTests extends WC_Unit_Test_Case {
'After the COT order record was deleted, the order was also deleted from the posts table.'
);
}
/**
* When sync is enabled, changes to meta data should propagate from the Custom Orders Table to
* the post meta table whenever the order object's save_meta_data() method is called.
*
* @return void
*/
public function test_meta_data_changes_propagate_from_cot_to_cpt(): void {
// Sync enabled and COT authoritative.
update_option( $this->sut::ORDERS_DATA_SYNC_ENABLED_OPTION, 'yes' );
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'yes' );
$order = OrderHelper::create_order();
$order->add_meta_data( 'foo', 'bar' );
$order->add_meta_data( 'bar', 'baz' );
$order->save_meta_data();
$order->delete_meta_data( 'bar' );
$order->save_meta_data();
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
$refreshed_order = wc_get_order( $order->get_id() );
$this->assertEquals(
$refreshed_order->get_meta( 'foo' ),
'bar',
'Meta data persisted via the HPOS datastore is accessible via the CPT datastore.'
);
$this->assertEquals(
$refreshed_order->get_meta( 'bar' ),
'',
'Meta data deleted from the HPOS datastore should also be deleted from the CPT datastore.'
);
}
/**
* When sync is enabled, changes to meta data should propagate from the post meta table to
* the Custom Orders Table whenever the order object's save_meta_data() method is called.
*
* @return void
*/
public function test_meta_data_changes_propagate_from_cpt_to_cot(): void {
// Sync enabled and CPT authoritative.
update_option( $this->sut::ORDERS_DATA_SYNC_ENABLED_OPTION, 'yes' );
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
$order = OrderHelper::create_order();
$order->add_meta_data( 'foo', 'bar' );
$order->add_meta_data( 'bar', 'baz' );
$order->save_meta_data();
$order->delete_meta_data( 'bar' );
$order->save_meta_data();
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'yes' );
$refreshed_order = wc_get_order( $order->get_id() );
$this->assertEquals(
$refreshed_order->get_meta( 'foo' ),
'bar',
'Meta data persisted via the CPT datastore is accessible via the HPOS datastore.'
);
$this->assertEquals(
$refreshed_order->get_meta( 'bar' ),
'',
'Meta data deleted from the CPT datastore should also be deleted from the HPOS datastore.'
);
}
}