Commit df1dc8f4fe5 for woocommerce
commit df1dc8f4fe575b39daa2f9880792a367b60b3ce4
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Fri Jul 24 13:00:33 2026 +0200
[Performance] Reduce the number of SQL queries on the cart page for multi-product carts (#66774)
This PR introduces the following changes:
- cache priming for variations when populating the cart from the session
- request-level cache for fetching tax rates in order to reduce the number of SQL queries on the cart page
diff --git a/plugins/woocommerce/changelog/perfromance-reduce-sqls-cart-page b/plugins/woocommerce/changelog/perfromance-reduce-sqls-cart-page
new file mode 100644
index 00000000000..f89b108fefc
--- /dev/null
+++ b/plugins/woocommerce/changelog/perfromance-reduce-sqls-cart-page
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Reduced the number of SQL queries on the cart page for multi-product carts.
diff --git a/plugins/woocommerce/includes/class-wc-cart-session.php b/plugins/woocommerce/includes/class-wc-cart-session.php
index c99bd9b39cd..676503adeb9 100644
--- a/plugins/woocommerce/includes/class-wc-cart-session.php
+++ b/plugins/woocommerce/includes/class-wc-cart-session.php
@@ -133,7 +133,9 @@ final class WC_Cart_Session {
if ( ! empty( $cart ) ) {
// Prime caches to reduce future queries.
- _prime_post_caches( wp_list_pluck( $cart, 'product_id' ) );
+ $product_ids = array_filter( array_column( $cart, 'product_id' ) );
+ $variations_ids = array_filter( array_column( $cart, 'variation_id' ) );
+ _prime_post_caches( array_merge( $product_ids, $variations_ids ) );
}
$cart_contents = array();
diff --git a/plugins/woocommerce/src/Internal/Tax/TaxRateDataStore.php b/plugins/woocommerce/src/Internal/Tax/TaxRateDataStore.php
index 9d48c9c15e1..09c69de3bd6 100644
--- a/plugins/woocommerce/src/Internal/Tax/TaxRateDataStore.php
+++ b/plugins/woocommerce/src/Internal/Tax/TaxRateDataStore.php
@@ -8,6 +8,12 @@ namespace Automattic\WooCommerce\Internal\Tax;
* Data store for tax rates.
*/
class TaxRateDataStore {
+ /**
+ * Request-level cache of fetched tax rate rows, keyed by tax_rate_id.
+ *
+ * @var array<int,object>
+ */
+ private array $rate_objects_cache = array();
/**
* Fetch multiple tax rate rows in a single query, keyed by tax_rate_id.
@@ -18,20 +24,26 @@ class TaxRateDataStore {
* @return array<int,object>
*/
public function get_rate_objects_for_ids( array $ids ): array {
- $tax_rate_objects = array();
- $ids = array_values( array_filter( array_map( 'absint', array_unique( $ids ) ) ) );
-
- if ( ! empty( $ids ) ) {
- global $wpdb;
+ global $wpdb;
- $list = implode( ', ', $ids );
+ $ids = array_filter( array_map( 'absint', array_unique( $ids ) ) );
+ $uncached_ids = array_diff( $ids, array_keys( $this->rate_objects_cache ) );
+ if ( ! empty( $uncached_ids ) ) {
+ $list = implode( ', ', $uncached_ids );
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$rows = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id IN ( $list )" );
foreach ( $rows as $row ) {
- $tax_rate_objects[ (int) $row->tax_rate_id ] = $row;
+ $this->rate_objects_cache[ (int) $row->tax_rate_id ] = $row;
+ }
+ }
+
+ $result = array();
+ foreach ( $ids as $id ) {
+ if ( isset( $this->rate_objects_cache[ $id ] ) ) {
+ $result[ $id ] = $this->rate_objects_cache[ $id ];
}
}
- return $tax_rate_objects;
+ return $result;
}
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Tax/TaxRateDataStoreTest.php b/plugins/woocommerce/tests/php/src/Internal/Tax/TaxRateDataStoreTest.php
index 14c176169f7..2248b157e24 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Tax/TaxRateDataStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Tax/TaxRateDataStoreTest.php
@@ -25,29 +25,46 @@ class TaxRateDataStoreTest extends \WC_Unit_Test_Case {
}
/**
- * @testdox get_rate_objects_for_ids() deduplicates mixed int/string IDs and returns a map keyed by int tax_rate_id.
+ * @testdox get_rate_objects_for_ids() deduplicates mixed int/string IDs, returns a map keyed by int tax_rate_id, and serves subsequent calls from the request-level cache.
*/
public function test_get_rate_objects_for_ids(): void {
- // Arrange.
- $tax_rate = array(
- 'tax_rate_country' => 'DE',
- 'tax_rate_state' => '',
- 'tax_rate' => '19.0000',
- 'tax_rate_name' => 'VAT',
- 'tax_rate_priority' => '1',
- 'tax_rate_compound' => '1',
- 'tax_rate_shipping' => '1',
- 'tax_rate_order' => '1',
- 'tax_rate_class' => '',
+ // Arrange: two GB rates as seen on a cart with standard + reduced VAT.
+ $standard_id = \WC_Tax::_insert_tax_rate(
+ array(
+ 'tax_rate_country' => 'GB',
+ 'tax_rate_state' => '',
+ 'tax_rate' => '20.0000',
+ 'tax_rate_name' => 'Standard Rate',
+ 'tax_rate_priority' => '1',
+ 'tax_rate_compound' => '0',
+ 'tax_rate_shipping' => '1',
+ 'tax_rate_order' => '1',
+ 'tax_rate_class' => '',
+ )
);
- $tax_rate_id = \WC_Tax::_insert_tax_rate( $tax_rate );
+ $reduced_id = \WC_Tax::_insert_tax_rate(
+ array(
+ 'tax_rate_country' => 'GB',
+ 'tax_rate_state' => '',
+ 'tax_rate' => '5.0000',
+ 'tax_rate_name' => 'Reduced Rate',
+ 'tax_rate_priority' => '1',
+ 'tax_rate_compound' => '0',
+ 'tax_rate_shipping' => '0',
+ 'tax_rate_order' => '2',
+ 'tax_rate_class' => 'reduced-rate',
+ )
+ );
+
+ // Act: deduplication — pass standard_id as both int and string, plus a non-existent ID.
+ $ids = array( $reduced_id, $standard_id, (string) $standard_id, PHP_INT_MAX );
+ $result = $this->sut->get_rate_objects_for_ids( $ids );
- // Act.
- $result = $this->sut->get_rate_objects_for_ids( array( $tax_rate_id, (string) $tax_rate_id, PHP_INT_MAX ) );
+ // Assert: both rates present, each keyed by its own ID with correct values.
+ $this->assertSame( array( $reduced_id, $standard_id ), array_keys( $result ) );
+ $this->assertSame( array( '5.0000', '20.0000' ), array_column( $result, 'tax_rate' ) );
- // Assert.
- $this->assertCount( 1, $result );
- $this->assertSame( array( $tax_rate_id ), array_keys( $result ) );
- $this->assertSame( 'VAT', $result[ $tax_rate_id ]->tax_rate_name );
+ // Verify a third call (full cache hit) returns identical result.
+ $this->assertSame( $result, $this->sut->get_rate_objects_for_ids( $ids ) );
}
}