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.
*/