Merge branch 'add/49335-related-products-collection' into add/pc-hand-picked-collection
This commit is contained in:
commit
06acb46aec
|
@ -21,6 +21,7 @@ import bestSellers from './best-sellers';
|
||||||
import onSale from './on-sale';
|
import onSale from './on-sale';
|
||||||
import featured from './featured';
|
import featured from './featured';
|
||||||
import handPicked from './hand-picked';
|
import handPicked from './hand-picked';
|
||||||
|
import related from './related';
|
||||||
|
|
||||||
const collections: BlockVariation[] = [
|
const collections: BlockVariation[] = [
|
||||||
productCollection,
|
productCollection,
|
||||||
|
@ -30,6 +31,7 @@ const collections: BlockVariation[] = [
|
||||||
bestSellers,
|
bestSellers,
|
||||||
newArrivals,
|
newArrivals,
|
||||||
handPicked,
|
handPicked,
|
||||||
|
related,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const registerCollections = () => {
|
export const registerCollections = () => {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import type { InnerBlockTemplate } from '@wordpress/blocks';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Icon, loop } from '@wordpress/icons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { INNER_BLOCKS_PRODUCT_TEMPLATE } from '../constants';
|
||||||
|
import { CoreCollectionNames, LayoutOptions } from '../types';
|
||||||
|
|
||||||
|
const collection = {
|
||||||
|
name: CoreCollectionNames.RELATED,
|
||||||
|
title: __( 'Related Products', 'woocommerce' ),
|
||||||
|
icon: <Icon icon={ loop } />,
|
||||||
|
description: __( 'Recommend products like this one.', 'woocommerce' ),
|
||||||
|
keywords: [ 'product collection' ],
|
||||||
|
scope: [],
|
||||||
|
usesReference: [ 'product' ],
|
||||||
|
};
|
||||||
|
|
||||||
|
const attributes = {
|
||||||
|
displayLayout: {
|
||||||
|
type: LayoutOptions.GRID,
|
||||||
|
columns: 4,
|
||||||
|
shrinkColumns: true,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
perPage: 4,
|
||||||
|
pages: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const heading: InnerBlockTemplate = [
|
||||||
|
'core/heading',
|
||||||
|
{
|
||||||
|
textAlign: 'center',
|
||||||
|
level: 2,
|
||||||
|
content: __( 'Related Products', 'woocommerce' ),
|
||||||
|
style: { spacing: { margin: { bottom: '1rem' } } },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const innerBlocks: InnerBlockTemplate[] = [
|
||||||
|
heading,
|
||||||
|
INNER_BLOCKS_PRODUCT_TEMPLATE,
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...collection,
|
||||||
|
attributes,
|
||||||
|
innerBlocks,
|
||||||
|
};
|
|
@ -159,6 +159,7 @@ export enum CoreCollectionNames {
|
||||||
ON_SALE = 'woocommerce/product-collection/on-sale',
|
ON_SALE = 'woocommerce/product-collection/on-sale',
|
||||||
TOP_RATED = 'woocommerce/product-collection/top-rated',
|
TOP_RATED = 'woocommerce/product-collection/top-rated',
|
||||||
HAND_PICKED = 'woocommerce/product-collection/hand-picked',
|
HAND_PICKED = 'woocommerce/product-collection/hand-picked',
|
||||||
|
RELATED = 'woocommerce/product-collection/related',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CoreFilterNames {
|
export enum CoreFilterNames {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
New "Related Products" Product Collection type.
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||||
|
|
||||||
use Automattic\WooCommerce\Blocks\Utils\ProductCollectionUtils;
|
use Automattic\WooCommerce\Blocks\Utils\ProductCollectionUtils;
|
||||||
|
use InvalidArgumentException;
|
||||||
use WP_Query;
|
use WP_Query;
|
||||||
use WC_Tax;
|
use WC_Tax;
|
||||||
|
|
||||||
|
@ -18,6 +19,14 @@ class ProductCollection extends AbstractBlock {
|
||||||
*/
|
*/
|
||||||
protected $block_name = 'product-collection';
|
protected $block_name = 'product-collection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An associative array of collection handlers.
|
||||||
|
*
|
||||||
|
* @var array<string, callable> $collection_handler_store
|
||||||
|
* Keys are collection names, values are callable handlers for custom collection behavior.
|
||||||
|
*/
|
||||||
|
protected $collection_handler_store = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Block with its attributes before it gets rendered
|
* The Block with its attributes before it gets rendered
|
||||||
*
|
*
|
||||||
|
@ -123,6 +132,8 @@ class ProductCollection extends AbstractBlock {
|
||||||
|
|
||||||
// Disable client-side-navigation if incompatible blocks are detected.
|
// Disable client-side-navigation if incompatible blocks are detected.
|
||||||
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
||||||
|
|
||||||
|
$this->register_core_collections();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -624,22 +635,34 @@ class ProductCollection extends AbstractBlock {
|
||||||
/**
|
/**
|
||||||
* Update the query for the product query block in Editor.
|
* Update the query for the product query block in Editor.
|
||||||
*
|
*
|
||||||
* @param array $args Query args.
|
* @param array $query Query args.
|
||||||
* @param WP_REST_Request $request Request.
|
* @param WP_REST_Request $request Request.
|
||||||
*/
|
*/
|
||||||
public function update_rest_query_in_editor( $args, $request ): array {
|
public function update_rest_query_in_editor( $query, $request ): array {
|
||||||
// Only update the query if this is a product collection block.
|
// Only update the query if this is a product collection block.
|
||||||
$is_product_collection_block = $request->get_param( 'isProductCollectionBlock' );
|
$is_product_collection_block = $request->get_param( 'isProductCollectionBlock' );
|
||||||
if ( ! $is_product_collection_block ) {
|
if ( ! $is_product_collection_block ) {
|
||||||
return $args;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this a preview mode request?
|
|
||||||
// If yes, short-circuit the query and return the preview query args.
|
|
||||||
$product_collection_query_context = $request->get_param( 'productCollectionQueryContext' );
|
$product_collection_query_context = $request->get_param( 'productCollectionQueryContext' );
|
||||||
$is_preview = $product_collection_query_context['previewState']['isPreview'] ?? false;
|
$collection_args = array(
|
||||||
if ( 'true' === $is_preview ) {
|
'name' => $product_collection_query_context['collection'] ?? '',
|
||||||
return $this->get_preview_query_args( $args, $request );
|
// The editor uses a REST query to grab product post types. This means we don't have a block
|
||||||
|
// instance to work with and the client needs to provide the location context.
|
||||||
|
'productCollectionLocation' => $request->get_param( 'productCollectionLocation' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allow collections to modify the collection arguments passed to the query builder.
|
||||||
|
$handlers = $this->collection_handler_store[ $collection_args['name'] ] ?? null;
|
||||||
|
if ( isset( $handlers['editor_args'] ) ) {
|
||||||
|
$collection_args = call_user_func( $handlers['editor_args'], $collection_args, $query, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
// When requested, short-circuit the query and return the preview query args.
|
||||||
|
$preview_state = $request->get_param( 'previewState' );
|
||||||
|
if ( isset( $preview_state['isPreview'] ) && 'true' === $preview_state['isPreview'] ) {
|
||||||
|
return $this->get_preview_query_args( $collection_args, $query, $request );
|
||||||
}
|
}
|
||||||
|
|
||||||
$orderby = $request->get_param( 'orderBy' );
|
$orderby = $request->get_param( 'orderBy' );
|
||||||
|
@ -652,10 +675,11 @@ class ProductCollection extends AbstractBlock {
|
||||||
$price_range = $request->get_param( 'priceRange' );
|
$price_range = $request->get_param( 'priceRange' );
|
||||||
// This argument is required for the tests to PHP Unit Tests to run correctly.
|
// This argument is required for the tests to PHP Unit Tests to run correctly.
|
||||||
// Most likely this argument is being accessed in the test environment image.
|
// Most likely this argument is being accessed in the test environment image.
|
||||||
$args['author'] = '';
|
$query['author'] = '';
|
||||||
|
|
||||||
$final_query_args = $this->get_final_query_args(
|
$final_query = $this->get_final_query_args(
|
||||||
$args,
|
$collection_args,
|
||||||
|
$query,
|
||||||
array(
|
array(
|
||||||
'collection' => $product_collection_query_context['collection'] ?? '',
|
'collection' => $product_collection_query_context['collection'] ?? '',
|
||||||
'orderby' => $orderby,
|
'orderby' => $orderby,
|
||||||
|
@ -669,7 +693,7 @@ class ProductCollection extends AbstractBlock {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $final_query_args;
|
return $final_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -726,28 +750,40 @@ class ProductCollection extends AbstractBlock {
|
||||||
|
|
||||||
$is_exclude_applied_filters = ! ( $inherit || $filterable );
|
$is_exclude_applied_filters = ! ( $inherit || $filterable );
|
||||||
|
|
||||||
return $this->get_final_frontend_query( $block_context_query, $page, $is_exclude_applied_filters );
|
$collection_args = array(
|
||||||
|
'name' => $block->context['collection'] ?? '',
|
||||||
|
'productCollectionLocation' => $block->context['productCollectionLocation'] ?? null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->get_final_frontend_query(
|
||||||
|
$collection_args,
|
||||||
|
$block_context_query,
|
||||||
|
$page,
|
||||||
|
$is_exclude_applied_filters
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the final query arguments for the frontend.
|
* Get the final query arguments for the frontend.
|
||||||
*
|
*
|
||||||
|
* @param array $collection_args Any special arguments that should change the behavior of the query.
|
||||||
* @param array $query The query arguments.
|
* @param array $query The query arguments.
|
||||||
* @param int $page The page number.
|
* @param int $page The page number.
|
||||||
* @param bool $is_exclude_applied_filters Whether to exclude the applied filters or not.
|
* @param bool $is_exclude_applied_filters Whether to exclude the applied filters or not.
|
||||||
*/
|
*/
|
||||||
private function get_final_frontend_query( $query, $page = 1, $is_exclude_applied_filters = false ) {
|
private function get_final_frontend_query( $collection_args, $query, $page = 1, $is_exclude_applied_filters = false ) {
|
||||||
|
$product_ids = $query['post__in'] ?? array();
|
||||||
$offset = $query['offset'] ?? 0;
|
$offset = $query['offset'] ?? 0;
|
||||||
$per_page = $query['perPage'] ?? 9;
|
$per_page = $query['perPage'] ?? 9;
|
||||||
|
|
||||||
$common_query_values = array(
|
$common_query_values = array(
|
||||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||||
'meta_query' => array(),
|
'meta_query' => array(),
|
||||||
'posts_per_page' => $query['perPage'],
|
'posts_per_page' => $per_page,
|
||||||
'order' => $query['order'],
|
'order' => $query['order'],
|
||||||
'offset' => ( $per_page * ( $page - 1 ) ) + $offset,
|
'offset' => ( $per_page * ( $page - 1 ) ) + $offset,
|
||||||
'post__in' => array(),
|
'post__in' => $product_ids,
|
||||||
'post_status' => 'publish',
|
'post_status' => 'publish',
|
||||||
'post_type' => 'product',
|
'post_type' => 'product',
|
||||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||||
|
@ -763,7 +799,14 @@ class ProductCollection extends AbstractBlock {
|
||||||
$time_frame = $query['timeFrame'] ?? null;
|
$time_frame = $query['timeFrame'] ?? null;
|
||||||
$price_range = $query['priceRange'] ?? null;
|
$price_range = $query['priceRange'] ?? null;
|
||||||
|
|
||||||
|
// Allow collections to modify the collection arguments passed to the query builder.
|
||||||
|
$handlers = $this->collection_handler_store[ $collection_args['name'] ] ?? null;
|
||||||
|
if ( isset( $handlers['frontend_args'] ) ) {
|
||||||
|
$collection_args = call_user_func( $handlers['frontend_args'], $collection_args, $query );
|
||||||
|
}
|
||||||
|
|
||||||
$final_query = $this->get_final_query_args(
|
$final_query = $this->get_final_query_args(
|
||||||
|
$collection_args,
|
||||||
$common_query_values,
|
$common_query_values,
|
||||||
array(
|
array(
|
||||||
'collection' => $this->parsed_block['attrs']['collection'] ?? '',
|
'collection' => $this->parsed_block['attrs']['collection'] ?? '',
|
||||||
|
@ -786,14 +829,19 @@ class ProductCollection extends AbstractBlock {
|
||||||
/**
|
/**
|
||||||
* Get final query args based on provided values
|
* Get final query args based on provided values
|
||||||
*
|
*
|
||||||
|
* @param array $collection_args Any special arguments that should change the behavior of the query.
|
||||||
* @param array $common_query_values Common query values.
|
* @param array $common_query_values Common query values.
|
||||||
* @param array $query Query from block context.
|
* @param array $query Query from block context.
|
||||||
* @param bool $is_exclude_applied_filters Whether to exclude the applied filters or not.
|
* @param bool $is_exclude_applied_filters Whether to exclude the applied filters or not.
|
||||||
*/
|
*/
|
||||||
private function get_final_query_args( $common_query_values, $query, $is_exclude_applied_filters = false ) {
|
private function get_final_query_args(
|
||||||
$handpicked_query = $this->get_handpicked_query( $query['handpicked_products'], $query['collection'] );
|
$collection_args,
|
||||||
$on_sale_query = $this->get_on_sale_products_query( $query['on_sale'] );
|
$common_query_values,
|
||||||
|
$query,
|
||||||
|
$is_exclude_applied_filters = false
|
||||||
|
) {
|
||||||
$orderby_query = $query['orderby'] ? $this->get_custom_orderby_query( $query['orderby'] ) : array();
|
$orderby_query = $query['orderby'] ? $this->get_custom_orderby_query( $query['orderby'] ) : array();
|
||||||
|
$on_sale_query = $this->get_on_sale_products_query( $query['on_sale'] );
|
||||||
$stock_query = $this->get_stock_status_query( $query['stock_status'] );
|
$stock_query = $this->get_stock_status_query( $query['stock_status'] );
|
||||||
$visibility_query = is_array( $query['stock_status'] ) ? $this->get_product_visibility_query( $stock_query, $query['stock_status'] ) : array();
|
$visibility_query = is_array( $query['stock_status'] ) ? $this->get_product_visibility_query( $stock_query, $query['stock_status'] ) : array();
|
||||||
$featured_query = $this->get_featured_query( $query['featured'] ?? false );
|
$featured_query = $this->get_featured_query( $query['featured'] ?? false );
|
||||||
|
@ -802,43 +850,54 @@ class ProductCollection extends AbstractBlock {
|
||||||
$tax_query = $this->merge_tax_queries( $visibility_query, $attributes_query, $taxonomies_query, $featured_query );
|
$tax_query = $this->merge_tax_queries( $visibility_query, $attributes_query, $taxonomies_query, $featured_query );
|
||||||
$date_query = $this->get_date_query( $query['timeFrame'] ?? array() );
|
$date_query = $this->get_date_query( $query['timeFrame'] ?? array() );
|
||||||
$price_query_args = $this->get_price_range_query_args( $query['priceRange'] ?? array() );
|
$price_query_args = $this->get_price_range_query_args( $query['priceRange'] ?? array() );
|
||||||
|
$handpicked_query = $this->get_handpicked_query( $query['handpicked_products'] ?? false );
|
||||||
|
|
||||||
// We exclude applied filters to generate product ids for the filter blocks.
|
// We exclude applied filters to generate product ids for the filter blocks.
|
||||||
$applied_filters_query = $is_exclude_applied_filters ? array() : $this->get_queries_by_applied_filters();
|
$applied_filters_query = $is_exclude_applied_filters ? array() : $this->get_queries_by_applied_filters();
|
||||||
|
|
||||||
$merged_query = $this->merge_queries(
|
// Allow collections to provide their own query parameters.
|
||||||
|
$handlers = $this->collection_handler_store[ $collection_args['name'] ] ?? null;
|
||||||
|
if ( isset( $handlers['build_query'] ) ) {
|
||||||
|
$collection_query = call_user_func(
|
||||||
|
$handlers['build_query'],
|
||||||
|
$collection_args,
|
||||||
|
$common_query_values,
|
||||||
|
$query,
|
||||||
|
$is_exclude_applied_filters
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$collection_query = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->merge_queries(
|
||||||
$common_query_values,
|
$common_query_values,
|
||||||
$orderby_query,
|
$orderby_query,
|
||||||
$handpicked_query,
|
|
||||||
$on_sale_query,
|
$on_sale_query,
|
||||||
$stock_query,
|
$stock_query,
|
||||||
$tax_query,
|
$tax_query,
|
||||||
$applied_filters_query,
|
$applied_filters_query,
|
||||||
$date_query,
|
$date_query,
|
||||||
$price_query_args
|
$price_query_args,
|
||||||
|
$handpicked_query,
|
||||||
|
$collection_query
|
||||||
);
|
);
|
||||||
|
|
||||||
return $merged_query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get query args for preview mode. These query args will be used with WP_Query to fetch the products.
|
* Get query args for preview mode. These query args will be used with WP_Query to fetch the products.
|
||||||
*
|
*
|
||||||
|
* @param array $collection_args Any collection-specific arguments.
|
||||||
* @param array $args Query args.
|
* @param array $args Query args.
|
||||||
* @param WP_REST_Request $request Request.
|
* @param WP_REST_Request $request Request.
|
||||||
*/
|
*/
|
||||||
private function get_preview_query_args( $args, $request ) {
|
private function get_preview_query_args( $collection_args, $args, $request ) {
|
||||||
$collection_query = array();
|
$collection_query = array();
|
||||||
|
|
||||||
/**
|
// Allow collections to override the preview mode behavior.
|
||||||
* In future, Here we will modify the preview query based on the collection name. For example:
|
$handlers = $this->collection_handler_store[ $collection_args['name'] ] ?? null;
|
||||||
*
|
if ( isset( $handlers['preview_query'] ) ) {
|
||||||
* $product_collection_query_context = $request->get_param( 'productCollectionQueryContext' );
|
$collection_query = call_user_func( $handlers['preview_query'], $collection_args, $args, $request );
|
||||||
* $collection_name = $product_collection_query_context['collection'] ?? '';
|
}
|
||||||
* if ( 'woocommerce/product-collection/on-sale' === $collection_name ) {
|
|
||||||
* $collection_query = $this->get_on_sale_products_query( true );
|
|
||||||
* }.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$args = $this->merge_queries( $args, $collection_query );
|
$args = $this->merge_queries( $args, $collection_query );
|
||||||
return $args;
|
return $args;
|
||||||
|
@ -867,38 +926,85 @@ class ProductCollection extends AbstractBlock {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function merge_queries( ...$queries ) {
|
private function merge_queries( ...$queries ) {
|
||||||
|
// Rather than a simple merge, some query vars should be held aside and merged differently.
|
||||||
|
$special_query_vars = array(
|
||||||
|
'post__in' => array(),
|
||||||
|
);
|
||||||
|
$special_query_keys = array_keys( $special_query_vars );
|
||||||
|
|
||||||
$merged_query = array_reduce(
|
$merged_query = array_reduce(
|
||||||
$queries,
|
$queries,
|
||||||
function ( $acc, $query ) {
|
function ( $acc, $query ) use ( $special_query_keys, &$special_query_vars ) {
|
||||||
if ( ! is_array( $query ) ) {
|
if ( ! is_array( $query ) ) {
|
||||||
return $acc;
|
return $acc;
|
||||||
}
|
}
|
||||||
// If the $query doesn't contain any valid query keys, we unpack/spread it then merge.
|
|
||||||
if ( empty( array_intersect( $this->get_valid_query_vars(), array_keys( $query ) ) ) ) {
|
// When the $query has keys but doesn't contain any valid query keys, we unpack/spread it then merge.
|
||||||
|
if ( ! empty( $query ) && empty( array_intersect( $this->get_valid_query_vars(), array_keys( $query ) ) ) ) {
|
||||||
return $this->merge_queries( $acc, ...array_values( $query ) );
|
return $this->merge_queries( $acc, ...array_values( $query ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->array_merge_recursive_replace_non_array_properties( $acc, $query );
|
// Pull out the special query vars so we can merge them separately.
|
||||||
|
foreach ( $special_query_keys as $query_var ) {
|
||||||
// The post__in query needs special handling, as it should be an
|
if ( isset( $query[ $query_var ] ) ) {
|
||||||
// intersection of all post__in queries.
|
$special_query_vars[ $query_var ][] = $query[ $query_var ];
|
||||||
if (
|
unset( $query[ $query_var ] );
|
||||||
is_array( $acc['post__in'] ?? null ) &&
|
}
|
||||||
! empty( $acc['post__in'] ) &&
|
|
||||||
is_array( $query['post__in'] ?? null ) &&
|
|
||||||
! empty( $query['post__in'] )
|
|
||||||
) {
|
|
||||||
$result['post__in'] = array_intersect( $acc['post__in'], $query['post__in'] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $this->array_merge_recursive_replace_non_array_properties( $acc, $query );
|
||||||
},
|
},
|
||||||
array()
|
array()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Perform any necessary special merges.
|
||||||
|
$merged_query['post__in'] = $this->merge_post__in( ...$special_query_vars['post__in'] );
|
||||||
|
|
||||||
return $merged_query;
|
return $merged_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge all of the 'post__in' values and return an array containing only values that are present in all arrays.
|
||||||
|
*
|
||||||
|
* @param int[][] ...$post__in The 'post__in' values to be merged.
|
||||||
|
*
|
||||||
|
* @return int[] The merged 'post__in' values.
|
||||||
|
*/
|
||||||
|
private function merge_post__in( ...$post__in ) {
|
||||||
|
if ( empty( $post__in ) ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we're using array_intersect, any array that is empty will result
|
||||||
|
// in an empty output array. To avoid this we need to make sure every
|
||||||
|
// argument is a non-empty array.
|
||||||
|
$post__in = array_filter(
|
||||||
|
$post__in,
|
||||||
|
function ( $val ) {
|
||||||
|
return is_array( $val ) && ! empty( $val );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if ( empty( $post__in ) ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the 'post__in' filter is exclusionary we need to use an intersection of
|
||||||
|
// all of the arrays. This ensures one query doesn't add options that another
|
||||||
|
// has otherwise excluded from the results.
|
||||||
|
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.
|
||||||
|
if ( empty( $post__in ) ) {
|
||||||
|
return array( -1 );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$post__in = reset( $post__in );
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values( array_unique( $post__in, SORT_NUMERIC ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return query params to support custom sort values
|
* Return query params to support custom sort values
|
||||||
*
|
*
|
||||||
|
@ -1175,6 +1281,23 @@ class ProductCollection extends AbstractBlock {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a post__in query to filter products to the set of provided IDs.
|
||||||
|
*
|
||||||
|
* @param int[]|false $handpicked_products The products to filter.
|
||||||
|
*
|
||||||
|
* @return array The post__in query.
|
||||||
|
*/
|
||||||
|
private function get_handpicked_query( $handpicked_products ) {
|
||||||
|
if ( false === $handpicked_products ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'post__in' => $handpicked_products,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge tax_queries from various queries.
|
* Merge tax_queries from various queries.
|
||||||
|
@ -1703,4 +1826,87 @@ class ProductCollection extends AbstractBlock {
|
||||||
|
|
||||||
return $price_filter + array_sum( $taxes );
|
return $price_filter + array_sum( $taxes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers handlers for a collection.
|
||||||
|
*
|
||||||
|
* @param string $collection_name The name of the custom collection.
|
||||||
|
* @param callable $build_query A hook returning any custom query arguments to merge with the collection's query.
|
||||||
|
* @param callable|null $frontend_args An optional hook that returns any frontend collection arguments to pass to the query builder.
|
||||||
|
* @param callable|null $editor_args An optional hook that returns any REST collection arguments to pass to the query builder.
|
||||||
|
* @param callable|null $preview_query An optional hook that returns a query to use in preview mode.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If collection handlers are already registered for the given collection name.
|
||||||
|
*/
|
||||||
|
protected function register_collection_handlers( $collection_name, $build_query, $frontend_args = null, $editor_args = null, $preview_query = null ) {
|
||||||
|
if ( isset( $this->collection_handler_store[ $collection_name ] ) ) {
|
||||||
|
throw new \InvalidArgumentException( 'Collection handlers already registered for ' . esc_html( $collection_name ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->collection_handler_store[ $collection_name ] = array(
|
||||||
|
'build_query' => $build_query,
|
||||||
|
'frontend_args' => $frontend_args,
|
||||||
|
'editor_args' => $editor_args,
|
||||||
|
'preview_query' => $preview_query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers any handlers for the core collections.
|
||||||
|
*/
|
||||||
|
protected function register_core_collections() {
|
||||||
|
$this->register_collection_handlers(
|
||||||
|
'woocommerce/product-collection/related',
|
||||||
|
function ( $collection_args ) {
|
||||||
|
// No products should be shown if no related product reference is set.
|
||||||
|
if ( empty( $collection_args['relatedProductReference'] ) ) {
|
||||||
|
return array(
|
||||||
|
'post__in' => array( -1 ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$related_products = wc_get_related_products(
|
||||||
|
$collection_args['relatedProductReference'],
|
||||||
|
// Use a higher limit so that the result set contains enough products for the collection to subsequently filter.
|
||||||
|
100
|
||||||
|
);
|
||||||
|
if ( empty( $related_products ) ) {
|
||||||
|
return array(
|
||||||
|
'post__in' => array( -1 ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have it filter the results to products related to the one provided.
|
||||||
|
return array(
|
||||||
|
'post__in' => $related_products,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function ( $collection_args, $query ) {
|
||||||
|
$product_reference = $query['productReference'] ?? null;
|
||||||
|
// Infer the product reference from the location if an explicit product is not set.
|
||||||
|
if ( empty( $product_reference ) ) {
|
||||||
|
$location = $collection_args['productCollectionLocation'];
|
||||||
|
if ( isset( $location['type'] ) && 'product' === $location['type'] ) {
|
||||||
|
$product_reference = $location['sourceData']['productId'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$collection_args['relatedProductReference'] = $product_reference;
|
||||||
|
return $collection_args;
|
||||||
|
},
|
||||||
|
function ( $collection_args, $query, $request ) {
|
||||||
|
$product_reference = $request->get_param( 'productReference' );
|
||||||
|
// In some cases the editor will send along block location context that we can infer the product reference from.
|
||||||
|
if ( empty( $product_reference ) ) {
|
||||||
|
$location = $collection_args['productCollectionLocation'];
|
||||||
|
if ( isset( $location['type'] ) && 'product' === $location['type'] ) {
|
||||||
|
$product_reference = $location['sourceData']['productId'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$collection_args['relatedProductReference'] = $product_reference;
|
||||||
|
return $collection_args;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,9 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
* Build the merged_query for testing
|
* Build the merged_query for testing
|
||||||
*
|
*
|
||||||
* @param array $parsed_block Parsed block data.
|
* @param array $parsed_block Parsed block data.
|
||||||
|
* @param array $query Query data.
|
||||||
*/
|
*/
|
||||||
private function initialize_merged_query( $parsed_block = array() ) {
|
private function initialize_merged_query( $parsed_block = array(), $query = array() ) {
|
||||||
if ( empty( $parsed_block ) ) {
|
if ( empty( $parsed_block ) ) {
|
||||||
$parsed_block = $this->get_base_parsed_block();
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
}
|
}
|
||||||
|
@ -71,8 +72,6 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
$block = new \stdClass();
|
$block = new \stdClass();
|
||||||
$block->context = $parsed_block['attrs'];
|
$block->context = $parsed_block['attrs'];
|
||||||
|
|
||||||
$query = build_query_vars_from_query_block( $block, 1 );
|
|
||||||
|
|
||||||
return $this->block_instance->build_frontend_query( $query, $block, 1 );
|
return $this->block_instance->build_frontend_query( $query, $block, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,14 +597,27 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
* - Product tags
|
* - Product tags
|
||||||
*/
|
*/
|
||||||
public function test_merging_taxonomies_query() {
|
public function test_merging_taxonomies_query() {
|
||||||
$parsed_block = $this->get_base_parsed_block();
|
$merged_query = $this->initialize_merged_query(
|
||||||
$parsed_block['attrs']['query']['taxQuery'] = array(
|
null,
|
||||||
'product_cat' => array( 1, 2 ),
|
// Since we aren't calling the Query Loop build function, we need to provide
|
||||||
'product_tag' => array( 3, 4 ),
|
// a tax_query rather than relying on it generating one from the input.
|
||||||
|
array(
|
||||||
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||||
|
'tax_query' => array(
|
||||||
|
array(
|
||||||
|
'taxonomy' => 'product_cat',
|
||||||
|
'terms' => array( 1, 2 ),
|
||||||
|
'include_children' => false,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'taxonomy' => 'product_tag',
|
||||||
|
'terms' => array( 3, 4 ),
|
||||||
|
'include_children' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$merged_query = $this->initialize_merged_query( $parsed_block );
|
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
array(
|
array(
|
||||||
'taxonomy' => 'product_cat',
|
'taxonomy' => 'product_cat',
|
||||||
|
@ -632,7 +644,9 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
$product_visibility_terms = wc_get_product_visibility_term_ids();
|
$product_visibility_terms = wc_get_product_visibility_term_ids();
|
||||||
$product_visibility_not_in = array( is_search() ? $product_visibility_terms['exclude-from-search'] : $product_visibility_terms['exclude-from-catalog'] );
|
$product_visibility_not_in = array( is_search() ? $product_visibility_terms['exclude-from-search'] : $product_visibility_terms['exclude-from-catalog'] );
|
||||||
|
|
||||||
$args = array();
|
$args = array(
|
||||||
|
'posts_per_page' => 9,
|
||||||
|
);
|
||||||
$request = $this->build_request();
|
$request = $this->build_request();
|
||||||
|
|
||||||
$updated_query = $this->block_instance->update_rest_query_in_editor( $args, $request );
|
$updated_query = $this->block_instance->update_rest_query_in_editor( $args, $request );
|
||||||
|
@ -666,7 +680,9 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
$product_visibility_terms = wc_get_product_visibility_term_ids();
|
$product_visibility_terms = wc_get_product_visibility_term_ids();
|
||||||
$product_visibility_not_in = array( is_search() ? $product_visibility_terms['exclude-from-search'] : $product_visibility_terms['exclude-from-catalog'] );
|
$product_visibility_not_in = array( is_search() ? $product_visibility_terms['exclude-from-search'] : $product_visibility_terms['exclude-from-catalog'] );
|
||||||
|
|
||||||
$args = array();
|
$args = array(
|
||||||
|
'posts_per_page' => 9,
|
||||||
|
);
|
||||||
$time_frame_date = gmdate( 'Y-m-d H:i:s' );
|
$time_frame_date = gmdate( 'Y-m-d H:i:s' );
|
||||||
$params = array(
|
$params = array(
|
||||||
'featured' => 'true',
|
'featured' => 'true',
|
||||||
|
@ -937,4 +953,240 @@ class ProductCollection extends \WP_UnitTestCase {
|
||||||
$this->assertStringContainsString( "( wc_product_meta_lookup.tax_class = 'collection-test' AND wc_product_meta_lookup.`min_price` >= 1.", $query->request );
|
$this->assertStringContainsString( "( wc_product_meta_lookup.tax_class = 'collection-test' AND wc_product_meta_lookup.`min_price` >= 1.", $query->request );
|
||||||
$this->assertStringContainsString( "( wc_product_meta_lookup.tax_class = 'collection-test' AND wc_product_meta_lookup.`max_price` <= 2.", $query->request );
|
$this->assertStringContainsString( "( wc_product_meta_lookup.tax_class = 'collection-test' AND wc_product_meta_lookup.`max_price` <= 2.", $query->request );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test handpicked products queries.
|
||||||
|
*/
|
||||||
|
public function test_handpicked_products_queries() {
|
||||||
|
$handpicked_product_ids = array( 1, 2, 3, 4 );
|
||||||
|
|
||||||
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
|
$parsed_block['attrs']['query']['woocommerceHandPickedProducts'] = $handpicked_product_ids;
|
||||||
|
|
||||||
|
$merged_query = $this->initialize_merged_query( $parsed_block );
|
||||||
|
|
||||||
|
foreach ( $handpicked_product_ids as $id ) {
|
||||||
|
$this->assertContainsEquals( $id, $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertCount( 4, $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test merging exclusive id filters.
|
||||||
|
*/
|
||||||
|
public function test_merges_post__in() {
|
||||||
|
$existing_id_filter = array( 1, 4 );
|
||||||
|
$handpicked_product_ids = array( 3, 4, 5, 6 );
|
||||||
|
// The only ID present in ALL of the exclusive filters is 4.
|
||||||
|
$expected_product_ids = array( 4 );
|
||||||
|
|
||||||
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
|
$parsed_block['attrs']['query']['post__in'] = $existing_id_filter;
|
||||||
|
$parsed_block['attrs']['query']['woocommerceHandPickedProducts'] = $handpicked_product_ids;
|
||||||
|
|
||||||
|
$merged_query = $this->initialize_merged_query( $parsed_block );
|
||||||
|
|
||||||
|
foreach ( $expected_product_ids as $id ) {
|
||||||
|
$this->assertContainsEquals( $id, $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertCount( 1, $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test merging exclusive id filters with no intersection.
|
||||||
|
*/
|
||||||
|
public function test_merges_post__in_empty_result_without_intersection() {
|
||||||
|
$existing_id_filter = array( 1, 4 );
|
||||||
|
$handpicked_product_ids = array( 2, 3 );
|
||||||
|
|
||||||
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
|
$parsed_block['attrs']['query']['post__in'] = $existing_id_filter;
|
||||||
|
$parsed_block['attrs']['query']['woocommerceHandPickedProducts'] = $handpicked_product_ids;
|
||||||
|
|
||||||
|
$merged_query = $this->initialize_merged_query( $parsed_block );
|
||||||
|
|
||||||
|
$this->assertEquals( array( -1 ), $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for frontend collection handlers.
|
||||||
|
*/
|
||||||
|
public function test_frontend_collection_handlers() {
|
||||||
|
$build_query = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
$frontend_args = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
$this->block_instance->register_collection_handlers( 'test-collection', $build_query, $frontend_args );
|
||||||
|
|
||||||
|
$frontend_args->expects( $this->once() )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->willReturnCallback(
|
||||||
|
function ( $collection_args ) {
|
||||||
|
$collection_args['test'] = 'test-arg';
|
||||||
|
return $collection_args;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$build_query->expects( $this->once() )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->willReturnCallback(
|
||||||
|
function ( $collection_args ) {
|
||||||
|
$this->assertArrayHasKey( 'test', $collection_args );
|
||||||
|
$this->assertEquals( 'test-arg', $collection_args['test'] );
|
||||||
|
return array(
|
||||||
|
'post__in' => array( 111 ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
|
$parsed_block['attrs']['collection'] = 'test-collection';
|
||||||
|
|
||||||
|
$merged_query = $this->initialize_merged_query( $parsed_block );
|
||||||
|
|
||||||
|
$this->block_instance->unregister_collection_handlers( 'test-collection' );
|
||||||
|
|
||||||
|
$this->assertContains( 111, $merged_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for editor collection handlers.
|
||||||
|
*/
|
||||||
|
public function test_editor_collection_handlers() {
|
||||||
|
$build_query = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
$editor_args = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
$this->block_instance->register_collection_handlers( 'test-collection', $build_query, null, $editor_args );
|
||||||
|
|
||||||
|
$editor_args->expects( $this->once() )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->willReturnCallback(
|
||||||
|
function ( $collection_args ) {
|
||||||
|
$collection_args['test'] = 'test-arg';
|
||||||
|
return $collection_args;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$build_query->expects( $this->once() )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->willReturnCallback(
|
||||||
|
function ( $collection_args ) {
|
||||||
|
$this->assertArrayHasKey( 'test', $collection_args );
|
||||||
|
$this->assertEquals( 'test-arg', $collection_args['test'] );
|
||||||
|
return array(
|
||||||
|
'post__in' => array( 111 ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = array();
|
||||||
|
$request = $this->build_request();
|
||||||
|
$request->set_param(
|
||||||
|
'productCollectionQueryContext',
|
||||||
|
array(
|
||||||
|
'collection' => 'test-collection',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$updated_query = $this->block_instance->update_rest_query_in_editor( $args, $request );
|
||||||
|
|
||||||
|
$this->block_instance->unregister_collection_handlers( 'test-collection' );
|
||||||
|
|
||||||
|
$this->assertContains( 111, $updated_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for the editor preview collection handler.
|
||||||
|
*/
|
||||||
|
public function test_editor_preview_collection_handler() {
|
||||||
|
$preview_query = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
$this->block_instance->register_collection_handlers(
|
||||||
|
'test-collection',
|
||||||
|
function () {
|
||||||
|
return array();
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$preview_query
|
||||||
|
);
|
||||||
|
|
||||||
|
$preview_query->expects( $this->once() )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->willReturn(
|
||||||
|
array(
|
||||||
|
'post__in' => array( 123 ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = array();
|
||||||
|
$request = $this->build_request();
|
||||||
|
$request->set_param(
|
||||||
|
'productCollectionQueryContext',
|
||||||
|
array(
|
||||||
|
'collection' => 'test-collection',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$request->set_param(
|
||||||
|
'previewState',
|
||||||
|
array(
|
||||||
|
'isPreview' => 'true',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$updated_query = $this->block_instance->update_rest_query_in_editor( $args, $request );
|
||||||
|
|
||||||
|
$this->block_instance->unregister_collection_handlers( 'test-collection' );
|
||||||
|
|
||||||
|
$this->assertContains( 123, $updated_query['post__in'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the related products collection handler works as expected.
|
||||||
|
*/
|
||||||
|
public function test_collection_related_products() {
|
||||||
|
$related_filter = $this->getMockBuilder( \stdClass::class )
|
||||||
|
->setMethods( [ '__invoke' ] )
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$expected_product_ids = array( 2, 3, 4 );
|
||||||
|
|
||||||
|
// This filter will turn off the data store so we don't need dummy products.
|
||||||
|
add_filter( 'woocommerce_product_related_posts_force_display', '__return_true', 0 );
|
||||||
|
$related_filter->expects( $this->exactly( 2 ) )
|
||||||
|
->method( '__invoke' )
|
||||||
|
->with( array(), 1 )
|
||||||
|
->willReturn( $expected_product_ids );
|
||||||
|
add_filter( 'woocommerce_related_products', array( $related_filter, '__invoke' ), 10, 2 );
|
||||||
|
|
||||||
|
// Frontend.
|
||||||
|
$parsed_block = $this->get_base_parsed_block();
|
||||||
|
$parsed_block['attrs']['collection'] = 'woocommerce/product-collection/related';
|
||||||
|
$parsed_block['attrs']['query']['productReference'] = 1;
|
||||||
|
$result_frontend = $this->initialize_merged_query( $parsed_block );
|
||||||
|
|
||||||
|
// Editor.
|
||||||
|
$request = $this->build_request(
|
||||||
|
array( 'productReference' => 1 )
|
||||||
|
);
|
||||||
|
$request->set_param(
|
||||||
|
'productCollectionQueryContext',
|
||||||
|
array(
|
||||||
|
'collection' => 'woocommerce/product-collection/related',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$result_editor = $this->block_instance->update_rest_query_in_editor( array(), $request );
|
||||||
|
|
||||||
|
remove_filter( 'woocommerce_product_related_posts_force_display', '__return_true', 0 );
|
||||||
|
remove_filter( 'woocommerce_related_products', array( $related_filter, '__invoke' ) );
|
||||||
|
|
||||||
|
$this->assertEqualsCanonicalizing( $expected_product_ids, $result_frontend['post__in'] );
|
||||||
|
$this->assertEqualsCanonicalizing( $expected_product_ids, $result_editor['post__in'] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ use Automattic\WooCommerce\Blocks\Assets\Api;
|
||||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||||
use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
|
use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
|
||||||
|
|
||||||
|
// phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProductCollectionMock used to test Product Query block functions.
|
* ProductCollectionMock used to test Product Query block functions.
|
||||||
*/
|
*/
|
||||||
|
@ -26,10 +28,10 @@ class ProductCollectionMock extends ProductCollection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For now don't need to initialize anything in tests so let's
|
* Override the normal initialization behavior to prevent registering the block with WordPress filters.
|
||||||
* just override the default behaviour.
|
|
||||||
*/
|
*/
|
||||||
protected function initialize() {
|
protected function initialize() {
|
||||||
|
$this->register_core_collections();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,4 +51,26 @@ class ProductCollectionMock extends ProductCollection {
|
||||||
public function set_attributes_filter_query_args( $data ) {
|
public function set_attributes_filter_query_args( $data ) {
|
||||||
$this->attributes_filter_query_args = $data;
|
$this->attributes_filter_query_args = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a protected method public so that it can be used in tests.
|
||||||
|
*
|
||||||
|
* @param string $collection_name The name of the custom collection.
|
||||||
|
* @param callable $build_query A hook returning any custom query arguments to merge with the collection's query.
|
||||||
|
* @param callable|null $frontend_args An optional hook that returns any frontend collection arguments to pass to the query builder.
|
||||||
|
* @param callable|null $editor_args An optional hook that returns any REST collection arguments to pass to the query builder.
|
||||||
|
* @param callable|null $preview_query An optional hook that returns a query to use in preview mode.
|
||||||
|
*/
|
||||||
|
public function register_collection_handlers( $collection_name, $build_query, $frontend_args = null, $editor_args = null, $preview_query = null ) {
|
||||||
|
parent::register_collection_handlers( $collection_name, $build_query, $frontend_args, $editor_args, $preview_query );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any custom collection handlers for the given collection.
|
||||||
|
*
|
||||||
|
* @param string $collection_name The name of the collection to unregister.
|
||||||
|
*/
|
||||||
|
public function unregister_collection_handlers( $collection_name ) {
|
||||||
|
unset( $this->collection_handlers[ $collection_name ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue