Commit 750d90b2985 for woocommerce
commit 750d90b2985cc226ae2075043189c48a9f881b91
Author: Mike Jolley <mike.jolley@me.com>
Date: Mon Oct 26 11:05:01 2026 +0000
Fix Store API filtering by taxonomies shared with other post types (#70124)
* Fix Store API filtering by taxonomies shared with other post types
ProductQuery built its _unstable_tax_* allowlist with get_taxonomies()
filtered on object_type, which only matches taxonomies registered for
exactly ['product']. Use get_object_taxonomies() so taxonomies also
registered for variations or other post types can be filtered on. The
'objects' output keeps the taxonomy-name keys the filter loop relies on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix Store API variation filtering by taxonomies registered for variations
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/44298-store-api-product-taxonomies b/plugins/woocommerce/changelog/44298-store-api-product-taxonomies
new file mode 100644
index 00000000000..7e594bf4139
--- /dev/null
+++ b/plugins/woocommerce/changelog/44298-store-api-product-taxonomies
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix Store API product filtering by custom taxonomies that are also registered for variations or other post types.
diff --git a/plugins/woocommerce/src/StoreApi/Utilities/ProductQuery.php b/plugins/woocommerce/src/StoreApi/Utilities/ProductQuery.php
index 8e7ba1ad272..18e49b1425e 100644
--- a/plugins/woocommerce/src/StoreApi/Utilities/ProductQuery.php
+++ b/plugins/woocommerce/src/StoreApi/Utilities/ProductQuery.php
@@ -110,11 +110,12 @@ class ProductQuery implements QueryClausesGenerator {
// Gets all registered product taxonomies and prefixes them with `tax_`.
// This is needed to avoid situations where a user registers a new product taxonomy with the same name as default field.
// eg an `sku` taxonomy will be mapped to `tax_sku`.
+ // The `objects` output is used because it is keyed by taxonomy name, which the loop below relies on.
$all_product_taxonomies = array_map(
- function ( $value ) {
- return '_unstable_tax_' . $value;
+ function ( $taxonomy ) {
+ return '_unstable_tax_' . $taxonomy->name;
},
- get_taxonomies( array( 'object_type' => array( 'product' ) ), 'names' )
+ get_object_taxonomies( 'product', 'objects' )
);
// Map between taxonomy name and arg key.
@@ -177,8 +178,19 @@ class ProductQuery implements QueryClausesGenerator {
if ( ! empty( $tax_query ) && 'product_variation' !== $args['post_type'] ) {
$args['tax_query'] = $tax_query; // phpcs:ignore
} else {
- // For product_variations we need to convert the tax_query to a meta_query.
- $args['meta_query'] = $this->convert_tax_query_to_meta_query( $tax_query ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
+ // Taxonomies registered for variations are queried directly. The rest are stored on variations as attribute meta.
+ $variation_tax_query = array_filter(
+ $tax_query,
+ function ( $tax_query_item ) {
+ return isset( $tax_query_item['taxonomy'] ) && is_object_in_taxonomy( 'product_variation', $tax_query_item['taxonomy'] );
+ }
+ );
+
+ if ( ! empty( $variation_tax_query ) ) {
+ $args['tax_query'] = array_values( $variation_tax_query ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
+ }
+
+ $args['meta_query'] = $this->convert_tax_query_to_meta_query( array_diff_key( $tax_query, $variation_tax_query ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
}
// Filter featured.
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/ProductQueryTest.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/ProductQueryTest.php
index 7d3dac1e3e8..3fa9f535d93 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/ProductQueryTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/ProductQueryTest.php
@@ -378,6 +378,60 @@ class ProductQueryTest extends \WC_Unit_Test_Case {
$this->assertSame( array( 0 ), $args['post__in'] );
}
+ /**
+ * @testdox Taxonomy filters work for product taxonomies that are also registered for other post types.
+ */
+ public function test_taxonomy_filter_supports_taxonomy_registered_for_multiple_object_types(): void {
+ $taxonomy = 'test_multi_object_tax';
+ register_taxonomy( $taxonomy, array( 'product', 'product_variation' ) );
+
+ try {
+ $fixtures = new FixtureData();
+ $alpha_product = $fixtures->get_simple_product( array( 'name' => 'Alpha product' ) );
+ $beta_product = $fixtures->get_simple_product( array( 'name' => 'Beta product' ) );
+ wp_set_object_terms( $alpha_product->get_id(), 'alpha', $taxonomy );
+ wp_set_object_terms( $beta_product->get_id(), 'beta', $taxonomy );
+
+ $request = new \WP_REST_Request( 'GET', '/wc/store/v1/products' );
+ $request->set_param( 'include', array( $alpha_product->get_id(), $beta_product->get_id() ) );
+ $request->set_param( '_unstable_tax_' . $taxonomy, array( 'alpha' ) );
+
+ $results = $this->product_query->get_results( $request );
+ } finally {
+ unregister_taxonomy( $taxonomy );
+ }
+
+ $this->assertSame( array( $alpha_product->get_id() ), $results['results'], 'Only the product assigned the requested term should be returned.' );
+ }
+
+ /**
+ * @testdox Taxonomy filters on variations use terms for taxonomies that are registered for variations.
+ */
+ public function test_variation_taxonomy_filter_supports_taxonomy_registered_for_variations(): void {
+ $taxonomy = 'test_multi_object_tax';
+ register_taxonomy( $taxonomy, array( 'product', 'product_variation' ) );
+
+ try {
+ $fixtures = new FixtureData();
+ $parent_product = $fixtures->get_variable_product( array() );
+ $alpha_variation = $fixtures->get_variation_product( $parent_product->get_id() );
+ $beta_variation = $fixtures->get_variation_product( $parent_product->get_id() );
+ wp_set_object_terms( $alpha_variation->get_id(), 'alpha', $taxonomy );
+ wp_set_object_terms( $beta_variation->get_id(), 'beta', $taxonomy );
+
+ $request = new \WP_REST_Request( 'GET', '/wc/store/v1/products' );
+ $request->set_param( 'type', 'variation' );
+ $request->set_param( 'include', array( $alpha_variation->get_id(), $beta_variation->get_id() ) );
+ $request->set_param( '_unstable_tax_' . $taxonomy, array( 'alpha' ) );
+
+ $results = $this->product_query->get_results( $request );
+ } finally {
+ unregister_taxonomy( $taxonomy );
+ }
+
+ $this->assertSame( array( $alpha_variation->get_id() ), $results['results'], 'Only the variation assigned the requested term should be returned.' );
+ }
+
/**
* Get product IDs matched by a Store API slug filter.
*