Commit 7fd068fee42 for woocommerce
commit 7fd068fee42f112a3c8709e68bbdfd9bf354300c
Author: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Mon Jul 6 18:46:51 2026 +0100
Revert "Performance: skip redundant meta existence checks on product create (#65595)" (#66325)
diff --git a/plugins/woocommerce/changelog/65595-fix-rest-batch-postmeta-create-fast-path b/plugins/woocommerce/changelog/65595-fix-rest-batch-postmeta-create-fast-path
deleted file mode 100644
index aedf64b4ba5..00000000000
--- a/plugins/woocommerce/changelog/65595-fix-rest-batch-postmeta-create-fast-path
+++ /dev/null
@@ -1,4 +0,0 @@
-Significance: patch
-Type: performance
-
-Improve product and variation creation performance by avoiding redundant internal meta existence checks.
\ No newline at end of file
diff --git a/plugins/woocommerce/changelog/revert-65595-product-create-meta-fast-path b/plugins/woocommerce/changelog/revert-65595-product-create-meta-fast-path
new file mode 100644
index 00000000000..48afdd8d1a5
--- /dev/null
+++ b/plugins/woocommerce/changelog/revert-65595-product-create-meta-fast-path
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Revert #65595 (product-create meta fast-path). Added and reverted within the 11.0 cycle, so no net user-facing change to document.
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-data-store-wp.php b/plugins/woocommerce/includes/data-stores/class-wc-data-store-wp.php
index cf941854c27..657d7c2c1d6 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-data-store-wp.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-data-store-wp.php
@@ -247,78 +247,24 @@ class WC_Data_Store_WP {
*
* Note: WordPress `get_metadata` function returns an empty string when meta data does not exist.
*
- * @param WC_Data $data_object The WP_Data object (WC_Coupon for coupons, etc).
+ * @param WC_Data $object The WP_Data object (WC_Coupon for coupons, etc).
* @param string $meta_key Meta key to update.
* @param mixed $meta_value Value to save.
- * @param bool $creating Whether the object is being created.
- * @param array $existing_meta_keys Existing meta keys map, maintained across calls during creation. Passed by reference.
+ *
* @since 3.6.0 Added to prevent empty meta being stored unless required.
*
- * @return bool True if added/updated/deleted.
+ * @return bool True if updated/deleted.
*/
- protected function update_or_delete_post_meta( $data_object, $meta_key, $meta_value, $creating = false, &$existing_meta_keys = null ) {
- $is_empty_meta_value = in_array( $meta_value, array( array(), '' ), true );
- $must_exist = in_array( $meta_key, $this->must_exist_meta_keys, true );
- $object_id = $data_object->get_id();
-
- if ( $creating ) {
- if ( ! is_array( $existing_meta_keys ) ) {
- $existing_meta_keys = $this->get_existing_meta_keys( $object_id );
- }
-
- $meta_exists = isset( $existing_meta_keys[ $meta_key ] );
-
- if ( $is_empty_meta_value && ! $must_exist ) {
- if ( ! $meta_exists ) {
- return false;
- }
-
- $updated = delete_post_meta( $object_id, $meta_key );
- unset( $existing_meta_keys[ $meta_key ] );
-
- return (bool) $updated;
- }
-
- if ( $meta_exists ) {
- $updated = update_post_meta( $object_id, $meta_key, $meta_value );
- } else {
- $updated = add_post_meta( $object_id, $meta_key, $meta_value );
- if ( $updated ) {
- $existing_meta_keys[ $meta_key ] = true;
- }
- }
-
- return (bool) $updated;
- }
-
- if ( $is_empty_meta_value && ! $must_exist ) {
- $updated = delete_post_meta( $object_id, $meta_key );
+ protected function update_or_delete_post_meta( $object, $meta_key, $meta_value ) {
+ if ( in_array( $meta_value, array( array(), '' ), true ) && ! in_array( $meta_key, $this->must_exist_meta_keys, true ) ) {
+ $updated = delete_post_meta( $object->get_id(), $meta_key );
} else {
- $updated = update_post_meta( $object_id, $meta_key, $meta_value );
+ $updated = update_post_meta( $object->get_id(), $meta_key, $meta_value );
}
return (bool) $updated;
}
- /**
- * Get the set of meta keys an object already has, to avoid per-key existence queries during creation.
- *
- * @param int $object_id Object ID.
- * @return array Map of existing meta key => true.
- */
- protected function get_existing_meta_keys( $object_id ) {
- global $wpdb;
-
- $meta_keys = $wpdb->get_col(
- $wpdb->prepare(
- "SELECT meta_key FROM {$wpdb->postmeta} WHERE post_id = %d",
- $object_id
- )
- );
-
- return array_fill_keys( $meta_keys, true );
- }
-
/**
* Get valid WP_Query args from a WC_Object_Query's query variables.
*
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
index 59798c645e5..de9b861600d 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
@@ -248,7 +248,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$post_object = get_post( $product->get_id() );
$product->set_status( $post_object->post_status );
- $this->update_post_meta_internal( $product, true, true );
+ $this->update_post_meta( $product, true );
$this->update_terms( $product, true );
$this->update_visibility( $product, true );
$this->update_attributes( $product, true );
@@ -697,31 +697,12 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
/**
* Helper method that updates all the post meta for a product based on it's settings in the WC_Product class.
*
- * Subclasses that override this method to write custom product-type meta should also override
- * update_post_meta_internal() so those writes run during object creation.
- *
* @param WC_Product $product Product object.
* @param bool $force Force update. Used during create.
* @since 3.0.0
* @return void
*/
protected function update_post_meta( &$product, $force = false ) {
- $this->update_post_meta_internal( $product, $force, false );
- }
-
- /**
- * Internal implementation of update_post_meta() that also knows whether the product is being created.
- *
- * Subclasses that override update_post_meta() to write custom product-type meta should also override
- * this method so those writes run during object creation.
- *
- * @param WC_Product $product Product object.
- * @param bool $force Force update. Used during create.
- * @param bool $creating Whether the product is being created.
- * @param array $existing_meta_keys Existing meta keys map, maintained across calls during creation. Passed by reference.
- * @return void
- */
- protected function update_post_meta_internal( &$product, $force, $creating, &$existing_meta_keys = null ) {
$meta_key_to_props = array(
'_sku' => 'sku',
'_global_unique_id' => 'global_unique_id',
@@ -807,7 +788,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
break;
}
- $updated = $this->update_or_delete_post_meta( $product, $meta_key, $value, $creating, $existing_meta_keys );
+ $updated = $this->update_or_delete_post_meta( $product, $meta_key, $value );
if ( $updated ) {
$this->updated_props[] = $prop;
@@ -829,7 +810,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$cogs_value = apply_filters( 'woocommerce_save_product_cogs_value', $cogs_value, $product );
if ( false !== $cogs_value ) {
- $updated = $this->update_or_delete_post_meta( $product, '_cogs_total_value', is_null( $cogs_value ) ? '' : $cogs_value, $creating, $existing_meta_keys );
+ $updated = $this->update_or_delete_post_meta( $product, '_cogs_total_value', is_null( $cogs_value ) ? '' : $cogs_value );
if ( $updated ) {
$this->updated_props[] = 'cogs_value';
}
@@ -847,7 +828,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
if ( is_callable( array( $product, $function ) ) ) {
$value = $product->{$function}( 'edit' );
$value = is_string( $value ) ? wp_slash( $value ) : $value;
- $updated = $this->update_or_delete_post_meta( $product, $meta_key, $value, $creating, $existing_meta_keys );
+ $updated = $this->update_or_delete_post_meta( $product, $meta_key, $value );
if ( $updated ) {
$this->updated_props[] = $key;
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-grouped-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-grouped-data-store-cpt.php
index 53188472c12..f21b9e6fa74 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-grouped-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-grouped-data-store-cpt.php
@@ -24,19 +24,6 @@ class WC_Product_Grouped_Data_Store_CPT extends WC_Product_Data_Store_CPT implem
* @since 3.0.0
*/
protected function update_post_meta( &$product, $force = false ) {
- $this->update_post_meta_internal( $product, $force, false );
- }
-
- /**
- * Internal implementation of update_post_meta() that also knows whether the product is being created.
- *
- * @param WC_Product $product Product object.
- * @param bool $force Force update. Used during create.
- * @param bool $creating Whether the product is being created.
- * @param array $existing_meta_keys Existing meta keys map, maintained across calls during creation. Passed by reference.
- * @return void
- */
- protected function update_post_meta_internal( &$product, $force, $creating, &$existing_meta_keys = null ) {
$meta_key_to_props = array(
'_children' => 'children',
);
@@ -51,7 +38,7 @@ class WC_Product_Grouped_Data_Store_CPT extends WC_Product_Data_Store_CPT implem
}
}
- parent::update_post_meta_internal( $product, $force, $creating, $existing_meta_keys );
+ parent::update_post_meta( $product, $force );
}
/**
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-variation-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-variation-data-store-cpt.php
index 6df569ac85a..731f2d0a242 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-variation-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-variation-data-store-cpt.php
@@ -20,18 +20,6 @@ if ( ! defined( 'ABSPATH' ) ) {
* @version 3.0.0
*/
class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT implements WC_Object_Data_Store_Interface {
- /**
- * Meta data which should exist in the DB, even if empty.
- *
- * @since 3.6.0
- *
- * @var array
- */
- protected $must_exist_meta_keys = array(
- '_tax_class',
- '_variation_description',
- );
-
/**
* Callback to remove unwanted meta data.
*
@@ -169,7 +157,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
if ( $id && ! is_wp_error( $id ) ) {
$product->set_id( $id );
- $this->update_post_meta_internal( $product, true, true );
+ $this->update_post_meta( $product, true );
$this->update_terms( $product, true );
$this->update_visibility( $product, true );
$this->update_attributes( $product, true );
@@ -607,19 +595,6 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
* @param bool $force Force update. Used during create.
*/
public function update_post_meta( &$product, $force = false ) {
- $this->update_post_meta_internal( $product, $force, false );
- }
-
- /**
- * Internal implementation of update_post_meta() that also knows whether the variation is being created.
- *
- * @since 3.0.0
- * @param WC_Product $product Product object.
- * @param bool $force Force update. Used during create.
- * @param bool $creating Whether the variation is being created.
- * @param array $existing_meta_keys Existing meta keys map, maintained across calls during creation. Passed by reference.
- */
- protected function update_post_meta_internal( &$product, $force, $creating, &$existing_meta_keys = null ) {
$meta_key_to_props = array(
'_variation_description' => 'description',
);
@@ -628,7 +603,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
foreach ( $props_to_update as $meta_key => $prop ) {
$value = $product->{"get_$prop"}( 'edit' );
- $updated = $this->update_or_delete_post_meta( $product, $meta_key, $value, $creating, $existing_meta_keys );
+ $updated = update_post_meta( $product->get_id(), $meta_key, $value );
if ( $updated ) {
$this->updated_props[] = $prop;
}
@@ -650,14 +625,14 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
$cogs_value_is_additive = apply_filters( 'woocommerce_save_product_cogs_is_additive_flag', $cogs_value_is_additive, $product );
if ( ! is_null( $cogs_value_is_additive ) ) {
- $updated = $this->update_or_delete_post_meta( $product, '_cogs_value_is_additive', $cogs_value_is_additive ? 'yes' : '', $creating, $existing_meta_keys );
+ $updated = $this->update_or_delete_post_meta( $product, '_cogs_value_is_additive', $cogs_value_is_additive ? 'yes' : '' );
if ( $updated ) {
$this->updated_props[] = 'cogs_value_is_additive';
}
}
}
- parent::update_post_meta_internal( $product, $force, $creating, $existing_meta_keys );
+ parent::update_post_meta( $product, $force );
}
/**
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
index 33a2a9e800d..27c688253fa 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
@@ -362,57 +362,6 @@ class WC_Product_Data_Store_CPT_Test extends WC_Unit_Test_Case {
$this->assertSame( '30.000000', get_post_meta( $product_id, '_stock', true ) );
}
- /**
- * @testdox Creating a product updates internal meta pre-created by save_post hooks instead of duplicating it.
- */
- public function test_create_updates_internal_meta_precreated_by_save_post_hooks(): void {
- $precreate_stock_meta = function ( $post_id, $post, $update ) {
- if ( $update || metadata_exists( 'post', $post_id, '_stock' ) ) {
- return;
- }
-
- add_post_meta( $post_id, '_manage_stock', 'no' );
- add_post_meta( $post_id, '_stock', '0' );
- };
-
- add_action( 'save_post_product', $precreate_stock_meta, 10, 3 );
-
- try {
- $product = new WC_Product();
- $product->set_name( 'Pre-created meta product' );
- $product->set_manage_stock( true );
- $product->set_stock_quantity( 5 );
- $product->save();
- } finally {
- remove_action( 'save_post_product', $precreate_stock_meta, 10 );
- }
-
- $product_id = $product->get_id();
-
- $this->assertSame( array( 'yes' ), get_post_meta( $product_id, '_manage_stock', false ) );
- $this->assertSame( array( '5' ), get_post_meta( $product_id, '_stock', false ) );
- $this->assertTrue( wc_get_product( $product_id )->get_manage_stock() );
- $this->assertSame( 5, wc_get_product( $product_id )->get_stock_quantity() );
- }
-
- /**
- * @testdox Creating a grouped product persists children on the initial save.
- */
- public function test_grouped_product_create_persists_children_on_initial_save(): void {
- $child_1 = WC_Helper_Product::create_simple_product();
- $child_2 = WC_Helper_Product::create_simple_product();
-
- $product = new WC_Product_Grouped();
- $product->set_name( 'Grouped product' );
- $product->set_children( array( $child_1->get_id(), $child_2->get_id() ) );
- $product->save();
-
- $product_id = $product->get_id();
-
- $this->assertSame( array( $child_1->get_id(), $child_2->get_id() ), get_post_meta( $product_id, '_children', true ) );
- $this->assertSame( array( $child_1->get_id(), $child_2->get_id() ), wc_get_product( $product_id )->get_children() );
- }
-
/**
* Test update_product_sales updates on the meta-entry.
*/
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variations-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variations-data-store-cpt-test.php
index b61bca2786a..d1fdb94a2dc 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variations-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variations-data-store-cpt-test.php
@@ -295,73 +295,6 @@ class WC_Product_Variation_Data_Store_CPT_Test extends WC_Unit_Test_Case {
$this->assertEquals( $flag_value ? 'yes' : '', get_post_meta( $product->get_id(), '_cogs_value_is_additive', true ) );
}
- /**
- * @testdox Creating a variation inserts known-new internal meta without update/delete hooks.
- */
- public function test_create_inserts_known_new_internal_meta_without_update_or_delete_hooks() {
- $parent = new WC_Product_Variable();
- $parent->set_name( 'Variable product' );
- $parent_id = $parent->save();
-
- $added_meta_events = array();
- $updated_meta_events = array();
- $deleted_meta_events = array();
-
- $added_meta_observer = function ( $meta_id, $object_id, $meta_key ) use ( &$added_meta_events ) {
- $added_meta_events[] = array( $object_id, $meta_key );
- };
-
- $updated_meta_observer = function ( $meta_id, $object_id, $meta_key ) use ( &$updated_meta_events ) {
- $updated_meta_events[] = array( $object_id, $meta_key );
- };
-
- $deleted_meta_observer = function ( $meta_ids, $object_id, $meta_key ) use ( &$deleted_meta_events ) {
- $deleted_meta_events[] = array( $object_id, $meta_key );
- };
-
- add_action( 'added_post_meta', $added_meta_observer, 10, 3 );
- add_action( 'updated_post_meta', $updated_meta_observer, 10, 3 );
- add_action( 'deleted_post_meta', $deleted_meta_observer, 10, 3 );
-
- try {
- $variation = new WC_Product_Variation();
- $variation->set_parent_id( $parent_id );
- $variation->set_sku( 'test-variation-fast-path' );
- $variation->set_regular_price( '12.50' );
- $variation->set_manage_stock( true );
- $variation->set_stock_quantity( 4 );
- $variation->save();
- } finally {
- remove_action( 'added_post_meta', $added_meta_observer, 10 );
- remove_action( 'updated_post_meta', $updated_meta_observer, 10 );
- remove_action( 'deleted_post_meta', $deleted_meta_observer, 10 );
- }
-
- $variation_id = $variation->get_id();
- $filter_to_variation_id = static function ( array $events ) use ( $variation_id ) {
- return array_values(
- array_map(
- static fn( $event ) => $event[1],
- array_filter( $events, static fn( $event ) => (int) $event[0] === $variation_id )
- )
- );
- };
-
- $added_meta_keys = $filter_to_variation_id( $added_meta_events );
- $updated_meta_keys = $filter_to_variation_id( $updated_meta_events );
- $deleted_meta_keys = $filter_to_variation_id( $deleted_meta_events );
-
- $this->assertContains( '_sku', $added_meta_keys );
- $this->assertContains( '_regular_price', $added_meta_keys );
- $this->assertContains( '_price', $added_meta_keys );
- $this->assertContains( '_manage_stock', $added_meta_keys );
- $this->assertContains( '_stock', $added_meta_keys );
- $this->assertContains( '_variation_description', $added_meta_keys );
- $this->assertEmpty( $updated_meta_keys );
- $this->assertEmpty( $deleted_meta_keys );
- $this->assertSame( array( '' ), get_post_meta( $variation_id, '_variation_description', false ) );
- }
-
/**
* Create a variable product and return one of its variations.
*