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 ) { foreach ( $order->get_meta_data() as $meta_data ) {
if ( isset( $existing_meta_data[ $meta_data->key ] ) ) { if ( isset( $existing_meta_data[ $meta_data->key ] ) ) {
if ( $existing_meta_data[ $meta_data->key ] === $meta_data->value ) { if ( $existing_meta_data[ $meta_data->key ] === $meta_data->value ) {
unset( $existing_meta_data[ $meta_data->key ] );
continue; continue;
} }
delete_post_meta( $order->get_id(), $meta_data->key );
unset( $existing_meta_data[ $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 ); 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 ); $this->update_post_meta( $order );
} }
} }

View File

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

View File

@ -11,6 +11,7 @@ use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
use Automattic\WooCommerce\Proxies\LegacyProxy; use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Utilities\ArrayUtil; use Automattic\WooCommerce\Utilities\ArrayUtil;
use Exception; use Exception;
use WC_Abstract_Order;
use WC_Data; use WC_Data;
use WC_Order; use WC_Order;
@ -1963,21 +1964,6 @@ FROM $order_meta_table
$order->delete_meta_data( '_wp_trash_meta_comments_status' ); $order->delete_meta_data( '_wp_trash_meta_comments_status' );
$order->save_meta_data(); $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; return true;
} }
@ -2457,9 +2443,17 @@ CREATE TABLE $meta_table (
* *
* @param WC_Data $object WC_Data object. * @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing at least ->id). * @param stdClass $meta (containing at least ->id).
*
* @return bool
*/ */
public function delete_meta( &$object, $meta ) { 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 WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->key and ->value). * @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 ) { 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 WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->id, ->key and ->value). * @param stdClass $meta (containing ->id, ->key and ->value).
*
* @return bool
*/ */
public function update_meta( &$object, $meta ) { 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.' '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.'
);
}
} }