Commit 239412da74 for woocommerce
commit 239412da74bcf10c3ffa07c5081b28c1ddaa423f
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Tue Feb 3 10:51:25 2026 +0100
[Performance] Products: reduce the number of SQLs when rendering related/upsell products. (#63006)
When rendering a product collection, priming caches at the collection level instead of the product level reduces the number of SQL queries. As a result, the number of SQL queries executed during cache priming remains constant, regardless of the collection size.
diff --git a/plugins/woocommerce/changelog/performance-62988-cache-priming-in-blocks b/plugins/woocommerce/changelog/performance-62988-cache-priming-in-blocks
new file mode 100644
index 0000000000..0aed678f56
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-62988-cache-priming-in-blocks
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Products: reduced the number of SQL queries required to display upsell and related products on the product page.
diff --git a/plugins/woocommerce/includes/wc-template-functions.php b/plugins/woocommerce/includes/wc-template-functions.php
index 70419b23c6..4b296c3ec8 100644
--- a/plugins/woocommerce/includes/wc-template-functions.php
+++ b/plugins/woocommerce/includes/wc-template-functions.php
@@ -2265,11 +2265,21 @@ if ( ! function_exists( 'woocommerce_related_products' ) ) {
$args = wp_parse_args( $args, $defaults );
- // Get visible related products then sort them at random.
- $args['related_products'] = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $args['posts_per_page'], $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' );
+ $related_products = array();
+ $related_product_ids = wc_get_related_products( $product->get_id(), $args['posts_per_page'], $product->get_upsell_ids() );
+ if ( ! empty( $related_product_ids ) ) {
+ // Optimization: reduce the number of SQLs needed to populate product objects.
+ _prime_post_caches( $related_product_ids );
- // Handle orderby.
- $args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] );
+ // Get visible related products then sort them at random, then handle orderby.
+ $related_products = array_filter( array_map( 'wc_get_product', $related_product_ids ), 'wc_products_array_filter_visible' );
+ $related_products = wc_products_array_orderby( $related_products, $args['orderby'], $args['order'] );
+ /** @var WC_Product[] $related_products */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
+
+ // Optimization: reduce the number of SQLs needed to fetch images when rendering.
+ _prime_post_caches( array_filter( array_map( fn( $product ) => (int) $product->get_image_id(), $related_products ) ) );
+ }
+ $args['related_products'] = $related_products;
// Set global loop values.
wc_set_loop_prop( 'name', 'related' );
@@ -2319,9 +2329,20 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
*/
$limit = intval( apply_filters( 'woocommerce_upsells_total', $args['posts_per_page'] ?? $limit ) );
- // Get visible upsells then sort them at random, then limit result set.
- $upsells = wc_products_array_orderby( array_filter( array_map( 'wc_get_product', $product->get_upsell_ids() ), 'wc_products_array_filter_visible' ), $orderby, $order );
- $upsells = $limit > 0 ? array_slice( $upsells, 0, $limit ) : $upsells;
+ $upsells = array();
+ $upsell_ids = $product->get_upsell_ids();
+ if ( ! empty( $upsell_ids ) ) {
+ // Optimization: reduce the number of SQLs needed to populate product objects.
+ _prime_post_caches( $upsell_ids );
+
+ // Get visible upsells then sort them at random, then limit result set.
+ $upsells = wc_products_array_orderby( array_filter( array_map( 'wc_get_product', $upsell_ids ), 'wc_products_array_filter_visible' ), $orderby, $order );
+ $upsells = $limit > 0 ? array_slice( $upsells, 0, $limit ) : $upsells;
+ /** @var WC_Product[] $upsells */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
+
+ // Optimization: reduce the number of SQLs needed to fetch images when rendering.
+ _prime_post_caches( array_filter( array_map( fn( $product ) => (int) $product->get_image_id(), $upsells ) ) );
+ }
wc_get_template(
'single-product/up-sells.php',
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/RelatedProducts.php b/plugins/woocommerce/src/Blocks/BlockTypes/RelatedProducts.php
index aa0c6ab848..a3f4a94969 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/RelatedProducts.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/RelatedProducts.php
@@ -145,7 +145,7 @@ class RelatedProducts extends AbstractBlock {
*/
private function is_related_products_block( $parsed_block, $rendered_block = null ) {
$is_product_collection_block = $rendered_block->context['query']['isProductCollectionBlock'] ?? false;
- if ( ProductQuery::is_woocommerce_variation( $parsed_block ) && isset( $parsed_block['attrs']['namespace'] ) && 'woocommerce/related-products' === $parsed_block['attrs']['namespace'] && ! $is_product_collection_block ) {
+ if ( ! $is_product_collection_block && 'woocommerce/related-products' === ( $parsed_block['attrs']['namespace'] ?? null ) && ProductQuery::is_woocommerce_variation( $parsed_block ) ) {
return true;
}
@@ -157,9 +157,9 @@ class RelatedProducts extends AbstractBlock {
* The logic is copied from the core function woocommerce_related_products. https://github.com/woocommerce/woocommerce/blob/ca49caabcba84ce9f60a03c6d3534ec14b350b80/plugins/woocommerce/includes/wc-template-functions.php/#L2039-L2074
*
* @param number $product_per_page Products per page.
- * @return array Products ids.
+ * @return int[] Products ids.
*/
- private function get_related_products_ids( $product_per_page = 5 ) {
+ private function get_related_products_ids( $product_per_page = 5 ): array {
global $post;
$product = wc_get_product( $post->ID );
@@ -168,16 +168,21 @@ class RelatedProducts extends AbstractBlock {
return array();
}
- $related_products = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $product_per_page, $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' );
- $related_products = wc_products_array_orderby( $related_products, 'rand', 'desc' );
+ $related_products_ids = wc_get_related_products( $product->get_id(), $product_per_page, $product->get_upsell_ids() );
+ if ( ! empty( $related_products_ids ) ) {
+ // Optimization: reduce the number of SQLs needed to populate product objects.
+ _prime_post_caches( $related_products_ids );
- $related_product_ids = array_map(
- function ( $product ) {
- return $product->get_id();
- },
- $related_products
- );
+ $related_products = array_filter( array_map( 'wc_get_product', $related_products_ids ), 'wc_products_array_filter_visible' );
+ $related_products = wc_products_array_orderby( $related_products, 'rand', 'desc' );
+ /** @var \WC_Product[] $related_products */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
+
+ // Optimization: reduce the number of SQLs needed to fetch images when rendering.
+ _prime_post_caches( array_filter( array_map( fn( $product ) => (int) $product->get_image_id(), $related_products ) ) );
+
+ $related_products_ids = array_map( fn( $product ) => $product->get_id(), $related_products );
+ }
- return $related_product_ids;
+ return $related_products_ids;
}
}