From 8beb36b8c573109e252fb9b16f5a8439f8f38075 Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Fri, 3 Feb 2023 15:05:47 +0530 Subject: [PATCH 1/6] Fix product categories & keyword filter not working --- src/BlockTypes/ProductQuery.php | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 6b63c16d489..a06fb96d5ab 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -161,7 +161,7 @@ public function build_query( $query ) { $this->get_global_query( $parsed_block ), $this->get_custom_orderby_query( $query['orderby'] ), $this->get_queries_by_attributes( $parsed_block ), - $this->get_queries_by_applied_filters() + $this->get_queries_by_applied_filters( $query ) ); } @@ -474,15 +474,29 @@ function( $acc, $attribute ) { /** * Return queries that are generated by query args. * + * @param array $query Query args. + * * @return array */ - private function get_queries_by_applied_filters() { - return array( + private function get_queries_by_applied_filters( $query ) { + $result = array( 'price_filter' => $this->get_filter_by_price_query(), 'attributes_filter' => $this->get_filter_by_attributes_query(), 'stock_status_filter' => $this->get_filter_by_stock_status_query(), 'rating_filter' => $this->get_filter_by_rating_query(), ); + + // "Product Categories" could be provided using "Filters" ToolsPanel available in Inspector Controls. + if ( isset( $query['tax_query'] ) ) { + $result['tax_query'] = $this->get_filter_by_product_categories_query( $query['tax_query'] ); + } + + // "Keyword" could be provided using "Filters" ToolsPanel available in Inspector Controls. + if ( isset( $query['s'] ) ) { + $result['s'] = $query['s']; + } + + return $result; } /** @@ -814,4 +828,36 @@ function( $rating ) use ( $product_visibility_terms ) { ); } + + /** + * User could provide "Product Categories" using "Filters" ToolsPanel available in Inspector Controls. + * We use this function to extract it's query from $tax_query. + * + * For example, this is how the query for product categories will look like in $tax_query array: + * Array + * ( + * [taxonomy] => product_cat + * [terms] => Array + * ( + * [0] => 36 + * ) + * ) + * + * @param array $tax_query Tax query. + * @return array + */ + private function get_filter_by_product_categories_query( $tax_query ) { + if ( ! is_array( $tax_query ) ) { + return array(); + } + + foreach ( $tax_query as $item ) { + if ( isset( $item['taxonomy'] ) && 'product_cat' === $item['taxonomy'] ) { + return array( $item ); + } + } + + return array(); + } + } From 37f0ba019953a498581649d4083af60fe29a90bd Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Fri, 3 Feb 2023 15:32:01 +0530 Subject: [PATCH 2/6] Fix Product tags filter not working --- src/BlockTypes/ProductQuery.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index a06fb96d5ab..c3046424ec4 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -486,9 +486,9 @@ private function get_queries_by_applied_filters( $query ) { 'rating_filter' => $this->get_filter_by_rating_query(), ); - // "Product Categories" could be provided using "Filters" ToolsPanel available in Inspector Controls. + // "Product Categories" & "Product Tags" could be provided using "Filters" ToolsPanel available in Inspector Controls. if ( isset( $query['tax_query'] ) ) { - $result['tax_query'] = $this->get_filter_by_product_categories_query( $query['tax_query'] ); + $result['tax_query'] = $this->get_filter_by_product_categories_or_tags_query( $query['tax_query'] ); } // "Keyword" could be provided using "Filters" ToolsPanel available in Inspector Controls. @@ -843,21 +843,27 @@ function( $rating ) use ( $product_visibility_terms ) { * ) * ) * + * For product categories, taxonomy would be "product_tag" + * * @param array $tax_query Tax query. * @return array */ - private function get_filter_by_product_categories_query( $tax_query ) { + private function get_filter_by_product_categories_or_tags_query( $tax_query ) { if ( ! is_array( $tax_query ) ) { return array(); } + $result = array(); foreach ( $tax_query as $item ) { - if ( isset( $item['taxonomy'] ) && 'product_cat' === $item['taxonomy'] ) { - return array( $item ); + if ( isset( $item['taxonomy'] ) && ( + 'product_cat' === $item['taxonomy'] || + 'product_tag' === $item['taxonomy'] + ) ) { + $result[] = $item; } } - return array(); + return $result; } } From db26bdb7d86434f367bf854e2d9b56141154a17b Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Tue, 14 Feb 2023 17:30:59 +0530 Subject: [PATCH 3/6] Make code readable by creating functions for the different parts of the query. Functions: - get_filter_by_product_categories_or_tags_query - get_filter_by_keyword_query Also rename the function `get_queries_by_attributes` to `get_queries_by_custom_attributes` to make it more clear what it does. --- src/BlockTypes/ProductQuery.php | 84 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index c3046424ec4..1068725d105 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -160,8 +160,10 @@ public function build_query( $query ) { $common_query_values, $this->get_global_query( $parsed_block ), $this->get_custom_orderby_query( $query['orderby'] ), - $this->get_queries_by_attributes( $parsed_block ), - $this->get_queries_by_applied_filters( $query ) + $this->get_queries_by_custom_attributes( $parsed_block ), + $this->get_queries_by_applied_filters(), + $this->get_filter_by_product_categories_or_tags_query( $query['tax_query'] ), + $this->get_filter_by_keyword_query( $query ) ); } @@ -182,7 +184,7 @@ private function get_products_ids_by_attributes( $parsed_block ) { 'meta_query' => array(), 'tax_query' => array(), ), - $this->get_queries_by_attributes( $parsed_block ), + $this->get_queries_by_custom_attributes( $parsed_block ), $this->get_global_query( $parsed_block ) ); @@ -220,8 +222,8 @@ function( $acc, $query ) { * duplicated items. */ if ( - ! empty( $merged_query['post__in'] ) && - count( $merged_query['post__in'] ) > count( array_unique( $merged_query['post__in'] ) ) + ! empty( $merged_query['post__in'] ) && + count( $merged_query['post__in'] ) > count( array_unique( $merged_query['post__in'] ) ) ) { $merged_query['post__in'] = array_unique( array_diff( @@ -337,8 +339,8 @@ private function get_stock_status_query( $stock_statii ) { * meta query for stock status. */ if ( - count( $stock_statii ) === count( $stock_status_options ) && - array_diff( $stock_statii, $stock_status_options ) === array_diff( $stock_status_options, $stock_statii ) + count( $stock_statii ) === count( $stock_status_options ) && + array_diff( $stock_statii, $stock_status_options ) === array_diff( $stock_status_options, $stock_statii ) ) { return array(); } @@ -474,29 +476,15 @@ function( $acc, $attribute ) { /** * Return queries that are generated by query args. * - * @param array $query Query args. - * * @return array */ - private function get_queries_by_applied_filters( $query ) { - $result = array( + private function get_queries_by_applied_filters() { + return array( 'price_filter' => $this->get_filter_by_price_query(), 'attributes_filter' => $this->get_filter_by_attributes_query(), 'stock_status_filter' => $this->get_filter_by_stock_status_query(), 'rating_filter' => $this->get_filter_by_rating_query(), ); - - // "Product Categories" & "Product Tags" could be provided using "Filters" ToolsPanel available in Inspector Controls. - if ( isset( $query['tax_query'] ) ) { - $result['tax_query'] = $this->get_filter_by_product_categories_or_tags_query( $query['tax_query'] ); - } - - // "Keyword" could be provided using "Filters" ToolsPanel available in Inspector Controls. - if ( isset( $query['s'] ) ) { - $result['s'] = $query['s']; - } - - return $result; } /** @@ -505,7 +493,7 @@ private function get_queries_by_applied_filters( $query ) { * @param array $parsed_block The Product Query that being rendered. * @return array */ - private function get_queries_by_attributes( $parsed_block ) { + private function get_queries_by_custom_attributes( $parsed_block ) { $query = $parsed_block['attrs']['query']; $on_sale_enabled = isset( $query['__woocommerceOnSale'] ) && true === $query['__woocommerceOnSale']; $attributes_query = isset( $query['__woocommerceAttributes'] ) ? $this->get_product_attributes_query( $query['__woocommerceAttributes'] ) : array(); @@ -633,15 +621,15 @@ function( $stock_status ) { return array( // Ignoring the warning of not using meta queries. - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( - array( - 'key' => '_stock_status', - 'value' => $filtered_stock_status_values, - 'operator' => 'IN', + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + array( + 'key' => '_stock_status', + 'value' => $filtered_stock_status_values, + 'operator' => 'IN', - ), ), + ), ); } @@ -848,22 +836,34 @@ function( $rating ) use ( $product_visibility_terms ) { * @param array $tax_query Tax query. * @return array */ - private function get_filter_by_product_categories_or_tags_query( $tax_query ) { + private function get_filter_by_product_categories_or_tags_query( $tax_query ): array { if ( ! is_array( $tax_query ) ) { - return array(); + return []; } - $result = array(); - foreach ( $tax_query as $item ) { - if ( isset( $item['taxonomy'] ) && ( - 'product_cat' === $item['taxonomy'] || - 'product_tag' === $item['taxonomy'] - ) ) { - $result[] = $item; + $product_taxonomies = [ 'product_cat', 'product_tag' ]; + $result = array_filter( + $tax_query, + function( $item ) use ( $product_taxonomies ) { + return isset( $item['taxonomy'] ) && in_array( $item['taxonomy'], $product_taxonomies, true ); } - } + ); - return $result; + return ! empty( $result ) ? [ 'tax_query' => $result ] : []; } + /** + * Returns the keyword filter from the given query. + * + * @param WP_Query $query The query to extract the keyword filter from. + * @return array The keyword filter, or an empty array if none is found. + * @throws InvalidArgumentException If $query is not an array. + */ + private function get_filter_by_keyword_query( $query ): array { + if ( isset( $query['s'] ) ) { + return [ 's' => $query['s'] ]; + } + + return []; + } } From e6bd862f5c06ae57eace987445c353d0c4442186 Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Fri, 17 Feb 2023 12:13:11 +0530 Subject: [PATCH 4/6] Make function generic by including all taxonomies As user can add custom taxonomies, we need to make sure that we include all taxonomies in the query. To fetch all product taxonomies, I used following function: ```php get_taxonomies( array( 'object_type' => array( 'product' ) ), 'names' ) ``` --- src/BlockTypes/ProductQuery.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 1068725d105..685323bdacf 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -162,7 +162,7 @@ public function build_query( $query ) { $this->get_custom_orderby_query( $query['orderby'] ), $this->get_queries_by_custom_attributes( $parsed_block ), $this->get_queries_by_applied_filters(), - $this->get_filter_by_product_categories_or_tags_query( $query['tax_query'] ), + $this->get_filter_by_taxonomies_query( $query['tax_query'] ), $this->get_filter_by_keyword_query( $query ) ); } @@ -818,6 +818,9 @@ function( $rating ) use ( $product_visibility_terms ) { /** + * Return a query to filter products by taxonomies (product categories, product tags, etc.) + * + * For example: * User could provide "Product Categories" using "Filters" ToolsPanel available in Inspector Controls. * We use this function to extract it's query from $tax_query. * @@ -833,15 +836,19 @@ function( $rating ) use ( $product_visibility_terms ) { * * For product categories, taxonomy would be "product_tag" * - * @param array $tax_query Tax query. - * @return array + * @param array $tax_query taxonomy query. + * @return array Query to filter products by taxonomies. */ - private function get_filter_by_product_categories_or_tags_query( $tax_query ): array { + private function get_filter_by_taxonomies_query( $tax_query ): array { if ( ! is_array( $tax_query ) ) { return []; } - $product_taxonomies = [ 'product_cat', 'product_tag' ]; + /** + * Get an array of taxonomy names associated with the "product" post type because + * we also want to include custom taxonomies associated with the "product" post type. + */ + $product_taxonomies = get_taxonomies( array( 'object_type' => array( 'product' ) ), 'names' ); $result = array_filter( $tax_query, function( $item ) use ( $product_taxonomies ) { From a5b248214b8b9d6721aead614378a4a48e50769e Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Fri, 17 Feb 2023 15:52:56 +0530 Subject: [PATCH 5/6] Minor improvements based on PR feedback --- src/BlockTypes/ProductQuery.php | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 685323bdacf..5dc2a9ccf41 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -162,7 +162,7 @@ public function build_query( $query ) { $this->get_custom_orderby_query( $query['orderby'] ), $this->get_queries_by_custom_attributes( $parsed_block ), $this->get_queries_by_applied_filters(), - $this->get_filter_by_taxonomies_query( $query['tax_query'] ), + $this->get_filter_by_taxonomies_query( isset( $query['tax_query'] ) ? $query['tax_query'] : [] ), $this->get_filter_by_keyword_query( $query ) ); } @@ -222,8 +222,8 @@ function( $acc, $query ) { * duplicated items. */ if ( - ! empty( $merged_query['post__in'] ) && - count( $merged_query['post__in'] ) > count( array_unique( $merged_query['post__in'] ) ) + ! empty( $merged_query['post__in'] ) && + count( $merged_query['post__in'] ) > count( array_unique( $merged_query['post__in'] ) ) ) { $merged_query['post__in'] = array_unique( array_diff( @@ -339,8 +339,8 @@ private function get_stock_status_query( $stock_statii ) { * meta query for stock status. */ if ( - count( $stock_statii ) === count( $stock_status_options ) && - array_diff( $stock_statii, $stock_status_options ) === array_diff( $stock_status_options, $stock_statii ) + count( $stock_statii ) === count( $stock_status_options ) && + array_diff( $stock_statii, $stock_status_options ) === array_diff( $stock_status_options, $stock_statii ) ) { return array(); } @@ -621,15 +621,14 @@ function( $stock_status ) { return array( // Ignoring the warning of not using meta queries. - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( - array( - 'key' => '_stock_status', - 'value' => $filtered_stock_status_values, - 'operator' => 'IN', - + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + array( + 'key' => '_stock_status', + 'value' => $filtered_stock_status_values, + 'operator' => 'IN', + ), ), - ), ); } @@ -864,9 +863,12 @@ function( $item ) use ( $product_taxonomies ) { * * @param WP_Query $query The query to extract the keyword filter from. * @return array The keyword filter, or an empty array if none is found. - * @throws InvalidArgumentException If $query is not an array. */ private function get_filter_by_keyword_query( $query ): array { + if ( ! is_array( $query ) ) { + return []; + } + if ( isset( $query['s'] ) ) { return [ 's' => $query['s'] ]; } From 4ad1ae9c80cfc2b0cd6ea2ab8843463ce6006e6e Mon Sep 17 00:00:00 2001 From: Manish Menaria Date: Fri, 17 Feb 2023 15:59:50 +0530 Subject: [PATCH 6/6] Move null check for tax_query to function --- src/BlockTypes/ProductQuery.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 5dc2a9ccf41..85805715cfd 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -162,7 +162,7 @@ public function build_query( $query ) { $this->get_custom_orderby_query( $query['orderby'] ), $this->get_queries_by_custom_attributes( $parsed_block ), $this->get_queries_by_applied_filters(), - $this->get_filter_by_taxonomies_query( isset( $query['tax_query'] ) ? $query['tax_query'] : [] ), + $this->get_filter_by_taxonomies_query( $query ), $this->get_filter_by_keyword_query( $query ) ); } @@ -835,14 +835,15 @@ function( $rating ) use ( $product_visibility_terms ) { * * For product categories, taxonomy would be "product_tag" * - * @param array $tax_query taxonomy query. + * @param array $query WP_Query. * @return array Query to filter products by taxonomies. */ - private function get_filter_by_taxonomies_query( $tax_query ): array { - if ( ! is_array( $tax_query ) ) { + private function get_filter_by_taxonomies_query( $query ): array { + if ( ! isset( $query['tax_query'] ) || ! is_array( $query['tax_query'] ) ) { return []; } + $tax_query = $query['tax_query']; /** * Get an array of taxonomy names associated with the "product" post type because * we also want to include custom taxonomies associated with the "product" post type.