Commit 45bcffc4aae for woocommerce

commit 45bcffc4aaed2beb9960fa888e121a5547f783aa
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date:   Thu Oct 1 17:11:31 2026 +0200

    [Performance] Bulk-prime variable products transients on products page in admin to reduce the number of SQL queries. (#69301)

    Performs bulk priming of variable product transients on the Products page in the admin area.
    Collapses N individual priming SQLs into a single one covering all variable products on the page.

diff --git a/plugins/woocommerce/changelog/performance-admin-variable-products-sqls-count-reduction b/plugins/woocommerce/changelog/performance-admin-variable-products-sqls-count-reduction
new file mode 100644
index 00000000000..1edab3094ba
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-admin-variable-products-sqls-count-reduction
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Bulk-prime variable products transients on products page in admin to reduce the number of SQL queries.
diff --git a/plugins/woocommerce/includes/admin/list-tables/class-wc-admin-list-table-products.php b/plugins/woocommerce/includes/admin/list-tables/class-wc-admin-list-table-products.php
index 620f3f87dec..7613c7481b1 100644
--- a/plugins/woocommerce/includes/admin/list-tables/class-wc-admin-list-table-products.php
+++ b/plugins/woocommerce/includes/admin/list-tables/class-wc-admin-list-table-products.php
@@ -67,6 +67,7 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
 		// Use hooks to prime various caches and improve products page performance.
 		add_action( 'load-edit.php', array( $this, 'prime_status_counts_cache' ) );
 		add_filter( 'the_posts', array( $this, 'prime_thumbnail_caches' ), 10, 2 );
+		add_filter( 'the_posts', array( $this, 'prime_variable_products_transients' ), 10, 2 );

 		$cogs_controller              = wc_get_container()->get( CostOfGoodsSoldController::class );
 		$this->cogs_is_enabled        = $cogs_controller->feature_is_enabled();
@@ -107,6 +108,7 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
 	 * Prime featured image caches for product queries to avoid individual queries during rendering.
 	 *
 	 * @since 10.9.0
+	 * @internal
 	 *
 	 * @param \WP_Post[] $posts Posts from WP Query.
 	 * @param \WP_Query  $query Current query.
@@ -120,6 +122,48 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
 		return $posts;
 	}

+	/**
+	 * Prime transient option caches for variable products in bulk to avoid per-product queries during rendering.
+	 *
+	 * @since 11.3.0
+	 * @internal
+	 *
+	 * @param \WP_Post[] $posts Posts from WP Query.
+	 * @param \WP_Query  $query Current query.
+	 * @return array
+	 */
+	public function prime_variable_products_transients( $posts, $query ) {
+		if ( $query instanceof \WP_Query && 'product' === $query->get( 'post_type' ) && ! wp_using_ext_object_cache() ) {
+			$options = array();
+			// Performance note: post caches has been already primed, hence the loop operates on in-memory data.
+			foreach ( $posts as $post ) {
+				$terms = $post instanceof \WP_Post ? get_object_term_cache( $post->ID, 'product_type' ) : null;
+				$terms = array_filter( is_array( $terms ) ? $terms : array(), static fn ( $term ) => $term instanceof \WP_Term ); // @phpstan-ignore instanceof.alwaysTrue (defensive checks against filters)
+				if ( ! empty( $terms ) && ProductType::VARIABLE === current( $terms )->slug ) {
+					$product_id = $post->ID;
+
+					// See also \WC_Product_Variable_Data_Store_CPT::read_product_data and sync with it when neccessary.
+					$options[] = '_transient_wc_var_prices_' . $product_id;
+					$options[] = '_transient_timeout_wc_var_prices_' . $product_id;
+					$options[] = '_transient_wc_product_children_' . $product_id;
+					$options[] = '_transient_timeout_wc_product_children_' . $product_id;
+					$options[] = '_transient_wc_child_has_weight_' . $product_id;
+					$options[] = '_transient_timeout_wc_child_has_weight_' . $product_id;
+					$options[] = '_transient_wc_child_has_dimensions_' . $product_id;
+					$options[] = '_transient_timeout_wc_child_has_dimensions_' . $product_id;
+					$options[] = '_transient_wc_related_' . $product_id;
+					$options[] = '_transient_timeout_wc_related_' . $product_id;
+				}
+			}
+
+			if ( ! empty( $options ) ) {
+				wp_prime_option_caches( $options );
+			}
+		}
+
+		return $posts;
+	}
+
 	/**
 	 * Render blank state.
 	 */