Commit 75f677505b8 for woocommerce
commit 75f677505b8a6579aa7646047eea8cb155b7c2c1
Author: Vincent Borgers <88669109+VincentBorgers@users.noreply.github.com>
Date: Mon Oct 12 12:58:39 2026 +0200
Show no products in the On Sale collection when nothing is on sale (#69695)
* Show no products in the On Sale collection when nothing is on sale
When no products were on sale, the on-sale query passed an empty post__in.
merge_post__in() drops empty arrays, so the filter was ignored and the
collection showed every product. Use array( -1 ), which the Product
Collection queries already use for no results.
* Use 0 instead of -1 when no products are on sale
WP_Query passes post__in through absint(), so -1 becomes 1 and a product
with ID 1 was returned. 0 matches no product, as in the products REST
endpoints and the sale_products shortcode.
* Return 0 from merge_post__in() when the filters do not overlap
The empty intersection returned array( -1 ), which WP_Query turns into 1
with absint(). Combining the on sale filter with hand-picked products that
include product 1 then returned that product.
diff --git a/plugins/woocommerce/changelog/fix-product-collection-on-sale-empty b/plugins/woocommerce/changelog/fix-product-collection-on-sale-empty
new file mode 100644
index 00000000000..91243349ed4
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-product-collection-on-sale-empty
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Product Collection: show no products in the On Sale collection when no products are on sale, instead of every product.
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
index f5cd17a0ec8..a0b45460736 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
@@ -939,8 +939,11 @@ class QueryBuilder {
return array();
}
+ $on_sale_ids = wc_get_product_ids_on_sale();
+
+ // An empty post__in is ignored when queries are merged, so use 0 to match no product.
return array(
- 'post__in' => wc_get_product_ids_on_sale(),
+ 'post__in' => empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids,
);
}
@@ -1155,9 +1158,9 @@ class QueryBuilder {
if ( count( $post__in ) > 1 ) {
$post__in = array_intersect( ...$post__in );
// An empty array means that there was no overlap between the filters and so
- // the query should return no results.
+ // the query should return no results. WP_Query applies absint(), so use 0.
if ( empty( $post__in ) ) {
- return array( -1 );
+ return array( 0 );
}
} else {
$post__in = reset( $post__in );
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
index 642e635be18..51461d96aab 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/QueryBuilder.php
@@ -76,6 +76,35 @@ class QueryBuilder extends \WP_UnitTestCase {
delete_transient( 'wc_products_onsale' );
}
+ /**
+ * @testdox Should return no products when no products are on sale.
+ */
+ public function test_merging_on_sale_queries_without_products_on_sale(): void {
+ set_transient( 'wc_products_onsale', array(), DAY_IN_SECONDS * 30 );
+
+ $parsed_block = Utils::get_base_parsed_block();
+ $parsed_block['attrs']['query']['woocommerceOnSale'] = true;
+
+ $merged_query = Utils::initialize_merged_query( $this->block_instance, $parsed_block );
+
+ $this->assertSame( array( 0 ), $merged_query['post__in'], 'The on sale filter should match no products when none are on sale.' );
+ }
+
+ /**
+ * @testdox Should return no products when no products are on sale and hand-picked products are set.
+ */
+ public function test_merging_on_sale_queries_without_products_on_sale_and_handpicked_products(): void {
+ set_transient( 'wc_products_onsale', array(), DAY_IN_SECONDS * 30 );
+
+ $parsed_block = Utils::get_base_parsed_block();
+ $parsed_block['attrs']['query']['woocommerceOnSale'] = true;
+ $parsed_block['attrs']['query']['woocommerceHandPickedProducts'] = array( 1, 2 );
+
+ $merged_query = Utils::initialize_merged_query( $this->block_instance, $parsed_block );
+
+ $this->assertSame( array( 0 ), $merged_query['post__in'], 'Hand-picked products should not be returned when none are on sale.' );
+ }
+
/**
* Test merging stock status queries.
*/
@@ -739,7 +768,7 @@ class QueryBuilder extends \WP_UnitTestCase {
$merged_query = Utils::initialize_merged_query( $this->block_instance, $parsed_block );
- $this->assertEquals( array( -1 ), $merged_query['post__in'] );
+ $this->assertEquals( array( 0 ), $merged_query['post__in'] );
}
/**