From b8bf17efb32caf70c484e45d2a1e90a90c1bec90 Mon Sep 17 00:00:00 2001 From: Luigi Date: Thu, 29 Sep 2022 18:32:26 +0200 Subject: [PATCH] Fix filter blocks: the data (e.g: max price or stock-status) match the variation #7245 fix filter blocks: the data (e.g: max price or stock-status) match the variation --- .../hooks/collections/use-collection-data.ts | 6 +- assets/js/blocks/attribute-filter/block.tsx | 5 ++ assets/js/blocks/price-filter/block.tsx | 5 ++ assets/js/blocks/stock-filter/block.tsx | 5 ++ src/BlockTypes/ProductQuery.php | 58 ++++++++++++++++--- 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/assets/js/base/context/hooks/collections/use-collection-data.ts b/assets/js/base/context/hooks/collections/use-collection-data.ts index fdce755b002..97c0ebecec2 100644 --- a/assets/js/base/context/hooks/collections/use-collection-data.ts +++ b/assets/js/base/context/hooks/collections/use-collection-data.ts @@ -3,8 +3,9 @@ */ import { useState, useEffect, useMemo } from '@wordpress/element'; import { useDebounce } from 'use-debounce'; -import { sortBy } from 'lodash'; +import { isEmpty, sortBy } from 'lodash'; import { useShallowEqual } from '@woocommerce/base-hooks'; +import { getSettingWithCoercion } from '@woocommerce/settings'; import { objectHasProp } from '@woocommerce/types'; /** @@ -46,6 +47,7 @@ interface UseCollectionDataProps { queryPrices?: boolean; queryStock?: boolean; queryState: Record< string, unknown >; + productIds?: number[]; } export const useCollectionData = ( { @@ -53,6 +55,7 @@ export const useCollectionData = ( { queryPrices, queryStock, queryState, + productIds, }: UseCollectionDataProps ) => { let context = useQueryStateContext(); context = `${ context }-collection-data`; @@ -145,6 +148,7 @@ export const useCollectionData = ( { per_page: undefined, orderby: undefined, order: undefined, + ...( ! isEmpty( productIds ) && { include: productIds } ), ...collectionDataQueryVars, }, shouldSelect: debouncedShouldSelect, diff --git a/assets/js/blocks/attribute-filter/block.tsx b/assets/js/blocks/attribute-filter/block.tsx index a7b2a13d266..45546f42e5a 100644 --- a/assets/js/blocks/attribute-filter/block.tsx +++ b/assets/js/blocks/attribute-filter/block.tsx @@ -100,6 +100,10 @@ const AttributeFilterBlock = ( { isString ); + const productIds = isEditor + ? [] + : getSettingWithCoercion( 'product_ids', [], Array.isArray ); + const [ hasSetFilterDefaultsFromUrl, setHasSetFilterDefaultsFromUrl ] = useState( false ); @@ -157,6 +161,7 @@ const AttributeFilterBlock = ( { ...queryState, attributes: filterAvailableTerms ? queryState.attributes : null, }, + productIds, } ); /** diff --git a/assets/js/blocks/price-filter/block.tsx b/assets/js/blocks/price-filter/block.tsx index 4e191b55658..3efafaaefe3 100644 --- a/assets/js/blocks/price-filter/block.tsx +++ b/assets/js/blocks/price-filter/block.tsx @@ -97,6 +97,10 @@ const PriceFilterBlock = ( { isBoolean ); + const productIds = isEditor + ? [] + : getSettingWithCoercion( 'product_ids', [], Array.isArray ); + const [ hasSetFilterDefaultsFromUrl, setHasSetFilterDefaultsFromUrl ] = useState( false ); @@ -106,6 +110,7 @@ const PriceFilterBlock = ( { const { results, isLoading } = useCollectionData( { queryPrices: true, queryState, + productIds, } ); const currency = getCurrencyFromPriceResponse( diff --git a/assets/js/blocks/stock-filter/block.tsx b/assets/js/blocks/stock-filter/block.tsx index 869f0f0bf4a..5fcd1e2b446 100644 --- a/assets/js/blocks/stock-filter/block.tsx +++ b/assets/js/blocks/stock-filter/block.tsx @@ -67,6 +67,10 @@ const StockStatusFilterBlock = ( { {} ); + const productIds = isEditor + ? [] + : getSettingWithCoercion( 'product_ids', [], Array.isArray ); + const STOCK_STATUS_OPTIONS = useRef( getSetting( 'hideOutOfStockItems', false ) ? otherStockStatusOptions @@ -98,6 +102,7 @@ const StockStatusFilterBlock = ( { useCollectionData( { queryStock: true, queryState, + productIds, } ); /** diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index edbbe217cd3..b3bd28e19f0 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -74,6 +74,7 @@ public function update_query( $pre_render, $parsed_block ) { // Set this so that our product filters can detect if it's a PHP template. $this->asset_data_registry->add( 'has_filterable_products', true, true ); $this->asset_data_registry->add( 'is_rendering_php_template', true, true ); + $this->asset_data_registry->add( 'product_ids', $this->get_products_ids_by_attributes( $parsed_block ), true ); add_filter( 'query_loop_block_query_vars', array( $this, 'build_query' ), @@ -119,19 +120,62 @@ public function build_query( $query ) { $queries_by_filters ), function( $acc, $query ) { - $acc['post__in'] = isset( $query['post__in'] ) ? $this->intersect_arrays_when_not_empty( $acc['post__in'], $query['post__in'] ) : $acc['post__in']; + return $this->merge_queries( $acc, $query ); + }, + $common_query_values + ); + } + /** + * Return the product ids based on the attributes. + * + * @param array $parsed_block The block being rendered. + * @return array + */ + private function get_products_ids_by_attributes( $parsed_block ) { + $queries_by_attributes = $this->get_queries_by_attributes( $parsed_block ); + + $query = array_reduce( + $queries_by_attributes, + function( $acc, $query ) { + return $this->merge_queries( $acc, $query ); + }, + array( + 'post_type' => 'product', + 'post__in' => array(), + 'post_status' => 'publish', + 'posts_per_page' => -1, // Ignoring the warning of not using meta queries. // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - $acc['meta_query'] = isset( $query['meta_query'] ) ? array_merge( $acc['meta_query'], array( $query['meta_query'] ) ) : $acc['meta_query']; + 'meta_query' => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query - $acc['tax_query'] = isset( $query['tax_query'] ) ? array_merge( $acc['tax_query'], array( $query['tax_query'] ) ) : $acc['tax_query']; - - return $acc; - }, - $common_query_values + 'tax_query' => array(), + ) ); + $products = new \WP_Query( $query ); + $post_ids = wp_list_pluck( $products->posts, 'ID' ); + + return $post_ids; + } + + /** + * Merge in the first parameter the keys "post_in", "meta_query" and "tax_query" of the second parameter. + * + * @param array $query1 The first query. + * @param array $query2 The second query. + * @return array + */ + private function merge_queries( $query1, $query2 ) { + $query1['post__in'] = isset( $query2['post__in'] ) ? $this->intersect_arrays_when_not_empty( $query1['post__in'], $query2['post__in'] ) : $query1['post__in']; + // Ignoring the warning of not using meta queries. + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + $query1['meta_query'] = isset( $query2['meta_query'] ) ? array_merge( $query1['meta_query'], array( $query2['meta_query'] ) ) : $query1['meta_query']; + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + $query1['tax_query'] = isset( $query2['tax_query'] ) ? array_merge( $query1['tax_query'], array( $query2['tax_query'] ) ) : $query1['tax_query']; + + return $query1; + }