Commit 761d4f5d6d for woocommerce

commit 761d4f5d6d453d3234dadbae5d2f63790f0e9f8d
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date:   Wed Jan 21 09:58:08 2026 +0100

    [Performance] Admin: reduce the number of SQL queries on orders page (#62820)

    This PR reduces the number of SQL queries needed to populate product collections:
    - Prime post caches in bulk to prevent individual retrieval later.
    - Optimize COGS data injection by using already fetched metadata instead of querying each individually.

diff --git a/plugins/woocommerce/changelog/performance-admin-reduce-sqls-number-on-roducts-page b/plugins/woocommerce/changelog/performance-admin-reduce-sqls-number-on-roducts-page
new file mode 100644
index 0000000000..45170e6d5a
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-admin-reduce-sqls-number-on-roducts-page
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+The number of SQL queries on the orders pages (admin and my account) has been reduced to improve performance.
diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php
index 6fcacd5576..df82b59003 100644
--- a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php
+++ b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php
@@ -421,7 +421,6 @@ abstract class WC_Data {
 			}
 		}

-		$this->maybe_read_meta_data();
 		$meta_data  = $this->get_meta_data();
 		$array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key, true );
 		$value      = $single ? '' : array();
@@ -450,7 +449,6 @@ abstract class WC_Data {
 	 * @return boolean
 	 */
 	public function meta_exists( $key = '' ) {
-		$this->maybe_read_meta_data();
 		$array_keys = wp_list_pluck( $this->get_meta_data(), 'key' );
 		return in_array( $key, $array_keys, true );
 	}
diff --git a/plugins/woocommerce/includes/class-wc-order-factory.php b/plugins/woocommerce/includes/class-wc-order-factory.php
index be167b6a53..f92699c197 100644
--- a/plugins/woocommerce/includes/class-wc-order-factory.php
+++ b/plugins/woocommerce/includes/class-wc-order-factory.php
@@ -71,8 +71,9 @@ class WC_Order_Factory {
 			return array();
 		}

-		$result              = array();
+		/** @var int[] $order_ids */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
 		$order_ids           = array_filter( array_map( array( __CLASS__, 'get_order_id' ), $order_ids ) );
+		$result              = array();
 		$original_order_sort = $order_ids;
 		$order_cache         = wc_get_container()->get( OrderCache::class );

@@ -91,6 +92,8 @@ class WC_Order_Factory {
 			$order_ids = $uncached_order_ids;
 		}

+		_prime_post_caches( $order_ids, false, true );
+
 		// We separate order list by class, since their datastore might be different.
 		$order_list_by_class = array();
 		$order_id_classnames = self::get_class_names_for_order_ids( $order_ids );
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 58ae1f7b27..e919f13a1a 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -15720,12 +15720,6 @@ parameters:
 			count: 1
 			path: includes/class-wc-order-factory.php

-		-
-			message: '#^Parameter \#1 \$input of function array_flip expects array\<int\|string\>, array\<int\<min, \-1\>\|int\<1, max\>\|true\> given\.$#'
-			identifier: argument.type
-			count: 1
-			path: includes/class-wc-order-factory.php
-
 		-
 			message: '#^Parameter \#1 \$order_id of static method WC_Order_Factory\:\:get_class_name_for_order_id\(\) expects int, int\<min, \-1\>\|int\<1, max\>\|true given\.$#'
 			identifier: argument.type
@@ -69753,12 +69747,6 @@ parameters:
 			count: 1
 			path: src/Internal/DataStores/Orders/LegacyDataHandler.php

-		-
-			message: '#^@param WC_Abstract_Order \$product does not accept actual type of parameter\: WC_Data\.$#'
-			identifier: parameter.phpDocType
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^@param WC_Order \$order does not accept actual type of parameter\: WC_Abstract_Order\.$#'
 			identifier: parameter.phpDocType
@@ -69801,12 +69789,6 @@ parameters:
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Call to an undefined method WC_Data\:\:set_cogs_total_value\(\)\.$#'
-			identifier: method.notFound
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Call to an undefined method WC_Data_Store\:\:update_user_by_order_id\(\)\.$#'
 			identifier: method.notFound
@@ -69831,42 +69813,18 @@ parameters:
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Cannot access offset 0 on stdClass\|true\.$#'
-			identifier: offsetAccess.nonOffsetAccessible
-			count: 2
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
-		-
-			message: '#^Cannot access property \$key on false\.$#'
-			identifier: property.nonObject
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Cannot access property \$meta_key on object\|true\.$#'
 			identifier: property.nonObject
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Cannot access property \$meta_value on false\.$#'
-			identifier: property.nonObject
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Cannot access property \$meta_value on object\|true\.$#'
 			identifier: property.nonObject
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Cannot access property \$value on false\.$#'
-			identifier: property.nonObject
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Cannot call method getOffsetTimestamp\(\) on WC_DateTime\|null\.$#'
 			identifier: method.nonObject
@@ -70134,12 +70092,6 @@ parameters:
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Parameter \#1 \$array_arg of function current expects array\|object, stdClass\|true given\.$#'
-			identifier: argument.type
-			count: 3
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Parameter \#1 \$function of function call_user_func_array expects callable\(\)\: mixed, array\{Abstract_WC_Order_Data_Store_CPT, non\-falsy\-string\} given\.$#'
 			identifier: argument.type
@@ -70236,24 +70188,12 @@ parameters:
 			count: 2
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Parameter \#2 \$meta of method Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableDataStoreMeta\:\:delete_meta\(\) expects stdClass, false given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Parameter \#2 \$meta of method Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableDataStoreMeta\:\:update_meta\(\) expects stdClass, WC_Meta_Data given\.$#'
 			identifier: argument.type
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php

-		-
-			message: '#^Parameter \#2 \$meta of method Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableDataStoreMeta\:\:update_meta\(\) expects stdClass, false given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableDataStore.php
-
 		-
 			message: '#^Parameter \#2 \$output of function get_object_taxonomies expects ''names''\|''objects'', ''object'' given\.$#'
 			identifier: argument.type
@@ -70554,12 +70494,6 @@ parameters:
 			count: 1
 			path: src/Internal/DataStores/Orders/OrdersTableRefundDataStore.php

-		-
-			message: '#^Cannot access offset 0 on stdClass\|true\.$#'
-			identifier: offsetAccess.nonOffsetAccessible
-			count: 1
-			path: src/Internal/DataStores/Orders/OrdersTableRefundDataStore.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableRefundDataStore\:\:create\(\) has no return type specified\.$#'
 			identifier: missingType.return
diff --git a/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php b/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php
index 50a349470b..6da2d56242 100644
--- a/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php
+++ b/plugins/woocommerce/src/Internal/DataStores/CustomMetaDataStore.php
@@ -204,7 +204,7 @@ abstract class CustomMetaDataStore {
 	 * @param \WC_Data $object Object ID.
 	 * @param string   $meta_key Meta key.
 	 *
-	 * @return \stdClass|bool Metadata object or FALSE if not found.
+	 * @return \stdClass[]|false Metadata object or FALSE if not found.
 	 */
 	public function get_metadata_by_key( &$object, string $meta_key ) {
 		global $wpdb;
diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
index 21c0087b7c..bd0881493c 100644
--- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
+++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php
@@ -1402,8 +1402,8 @@ WHERE

 			$this->init_order_record( $order, $order_id, $order_data );

-			if ( $order->has_cogs() && $cogs_is_enabled ) {
-				$this->read_cogs_data( $order );
+			if ( $cogs_is_enabled && $order->has_cogs() ) {
+				$this->read_cogs_data( $order, $order_data->meta_data );
 			}

 			if ( $data_sync_enabled && isset( $post_orders[ $order_id ] ) && $this->should_sync_order( $order ) ) {
@@ -1416,11 +1416,12 @@ WHERE
 	/**
 	 * Read the Cost of Goods Sold value for a given order from the database, if available, and apply it to the order.
 	 *
-	 * @param \WC_Abstract_Order $order The order to get the COGS value for.
+	 * @param \WC_Abstract_Order                          $order     The order to get the COGS value for.
+	 * @param object{meta_key:string,meta_value:string}[] $meta_data The original meta-data fetched for the order.
 	 */
-	private function read_cogs_data( WC_Abstract_Order $order ) {
-		$meta_entry = $this->data_store_meta->get_metadata_by_key( $order, '_cogs_total_value' );
-		$cogs_value = false === $meta_entry ? 0 : (float) current( $meta_entry )->meta_value;
+	private function read_cogs_data( WC_Abstract_Order $order, array $meta_data ) {
+		$meta_entry = array_filter( $meta_data, fn( object $meta ) => '_cogs_total_value' === $meta->meta_key );
+		$cogs_value = array() === $meta_entry ? 0 : (float) current( $meta_entry )->meta_value;

 		/**
 		 * Filter to customize the Cost of Goods Sold value that gets loaded for a given order.