diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx index c203cf3ad31..d7728c7f209 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx @@ -37,6 +37,14 @@ const orderOptions = [ label: __( 'Oldest to newest', 'woocommerce' ), value: 'date/asc', }, + { + label: __( 'Price, high to low', 'woocommerce' ), + value: 'price/desc', + }, + { + label: __( 'Price, low to high', 'woocommerce' ), + value: 'price/asc', + }, { value: 'popularity/desc', label: __( 'Best Selling', 'woocommerce' ), diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts index 135e5b72c9e..c64689cc2f7 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts @@ -144,6 +144,7 @@ export type TProductCollectionOrderBy = | 'date' | 'title' | 'popularity' + | 'price' | 'rating'; export type ProductCollectionSetAttributes = ( diff --git a/plugins/woocommerce/changelog/add-product-collection-price-sorting-option b/plugins/woocommerce/changelog/add-product-collection-price-sorting-option new file mode 100644 index 00000000000..f47488471ed --- /dev/null +++ b/plugins/woocommerce/changelog/add-product-collection-price-sorting-option @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Product Collection: add sorting options by price. diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php index 345954899ac..2ae234117a7 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php @@ -53,7 +53,7 @@ class ProductCollection extends AbstractBlock { * * @var array */ - protected $custom_order_opts = array( 'popularity', 'rating', 'post__in' ); + protected $custom_order_opts = array( 'popularity', 'rating', 'post__in', 'price' ); /** @@ -706,7 +706,7 @@ class ProductCollection extends AbstractBlock { return $this->get_preview_query_args( $collection_args, $query, $request ); } - $orderby = $request->get_param( 'orderBy' ); + $orderby = $request->get_param( 'orderby' ); $on_sale = $request->get_param( 'woocommerceOnSale' ) === 'true'; $stock_status = $request->get_param( 'woocommerceStockStatus' ); $product_attributes = $request->get_param( 'woocommerceAttributes' ); @@ -1056,6 +1056,14 @@ class ProductCollection extends AbstractBlock { return array( 'orderby' => $orderby ); } + if ( 'price' === $orderby ) { + add_filter( 'posts_clauses', array( $this, 'add_price_sorting_posts_clauses' ), 10, 2 ); + return array( + 'isProductCollection' => true, + 'orderby' => $orderby, + ); + } + $meta_keys = array( 'popularity' => 'total_sales', 'rating' => '_wc_average_rating', @@ -1729,6 +1737,36 @@ class ProductCollection extends AbstractBlock { return $clauses; } + /** + * Add the `posts_clauses` filter to add price-based sorting + * + * @param array $clauses The list of clauses for the query. + * @param WP_Query $query The WP_Query instance. + * @return array Modified list of clauses. + */ + public function add_price_sorting_posts_clauses( $clauses, $query ) { + $query_vars = $query->query_vars; + $is_product_collection_block = $query_vars['isProductCollection'] ?? false; + + if ( ! $is_product_collection_block ) { + return $clauses; + } + + $orderby = $query_vars['orderby'] ?? null; + if ( 'price' !== $orderby ) { + return $clauses; + } + + $clauses['join'] = $this->append_product_sorting_table_join( $clauses['join'] ); + $is_ascending_order = 'asc' === strtolower( $query_vars['order'] ?? 'desc' ); + + $clauses['orderby'] = $is_ascending_order ? + 'wc_product_meta_lookup.min_price ASC, wc_product_meta_lookup.product_id ASC' : + 'wc_product_meta_lookup.max_price DESC, wc_product_meta_lookup.product_id DESC'; + + return $clauses; + } + /** * Determines if price filters need adjustment based on the tax display settings. * @@ -1964,7 +2002,7 @@ class ProductCollection extends AbstractBlock { $product->get_upsell_ids() ); }, - [] + array() ); // Remove duplicates and product references. We don't want to display diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection.php index 827f7ddc421..02fc3ee1b97 100644 --- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection.php +++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection.php @@ -1220,4 +1220,24 @@ class ProductCollection extends \WP_UnitTestCase { $this->assertEqualsCanonicalizing( $expected_product_ids, $result_frontend['post__in'] ); $this->assertEqualsCanonicalizing( $expected_product_ids, $result_editor['post__in'] ); } + + /** + * Test the add_price_sorting_posts_clauses method. + */ + public function test_add_price_sorting_posts_clauses() { + $parsed_block = $this->get_base_parsed_block(); + $parsed_block['attrs']['query']['orderBy'] = 'price'; + + $parsed_block['attrs']['query']['order'] = 'asc'; + $merged_query = $this->initialize_merged_query( $parsed_block ); + $query = new WP_Query( $merged_query ); + + $this->assertStringContainsString( 'wc_product_meta_lookup.min_price ASC', $query->request ); + + $parsed_block['attrs']['query']['order'] = 'desc'; + $merged_query = $this->initialize_merged_query( $parsed_block ); + $query = new WP_Query( $merged_query ); + + $this->assertStringContainsString( 'wc_product_meta_lookup.max_price DESC', $query->request ); + } }