Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Making filter block contextual on the front end #10919

Merged
merged 25 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7dc741e
Add collection filters block
roykho Aug 24, 2023
3abd242
Remove frontend script
roykho Aug 30, 2023
c8d2b06
use inner blocks hook
dinhtungdu Sep 8, 2023
9554855
Merge branch 'trunk' into filter-collection-block
dinhtungdu Sep 8, 2023
204caca
CollectionFilters: Hydrate Collection Data for inner filter blocks
dinhtungdu Sep 8, 2023
dca4d78
wip
dinhtungdu Sep 12, 2023
eca8acf
only alter context for filter inner blocks
dinhtungdu Sep 12, 2023
c275ea0
cache the current response
dinhtungdu Sep 12, 2023
156089c
fix block list iteration
dinhtungdu Sep 12, 2023
e413a30
refactor api hydration
dinhtungdu Sep 12, 2023
6c2f1f8
Merge branch 'trunk' into contextual-filter
dinhtungdu Sep 18, 2023
da465c7
map all product collection block query params to product collection d…
dinhtungdu Sep 18, 2023
133790e
support custom taxonomies
dinhtungdu Sep 18, 2023
dcb70d5
better explain the logic
dinhtungdu Sep 20, 2023
dadcb89
try: process shared params separately
dinhtungdu Sep 21, 2023
d6b6ffe
reduce if conditions
dinhtungdu Sep 21, 2023
9fa65fc
try: array walk
dinhtungdu Sep 21, 2023
dcfbfd9
Revert "try: array walk"
dinhtungdu Sep 21, 2023
de14d21
refactoring
dinhtungdu Sep 23, 2023
96eb82c
use array walk for explicit
dinhtungdu Sep 24, 2023
c41ad35
typo
dinhtungdu Oct 1, 2023
f58365b
Merge branch 'trunk' into contextual-filter
dinhtungdu Oct 3, 2023
74e7ecd
Merge branch 'trunk' into contextual-filter
dinhtungdu Oct 5, 2023
1ef3280
passing collection data context to filter block only
dinhtungdu Oct 5, 2023
2ece513
Merge branch 'trunk' into contextual-filter
dinhtungdu Oct 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/js/blocks/collection-filters/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"html": false,
"reusable": false
},
"usesContext": [ "query" ],
"ancestor": [ "woocommerce/product-collection" ],
"apiVersion": 2,
"$schema": "https://schemas.wp.org/trunk/block.json"
Expand Down
136 changes: 114 additions & 22 deletions src/BlockTypes/CollectionFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public function modify_inner_blocks_context( $context, $parsed_block, $parent_bl
$context['isCollectionFiltersInnerBlock'] = true;

if (
isset( $parsed_block['blockName'] ) ||
isset( $parsed_block['blockName'] ) &&
in_array( $parsed_block['blockName'], $this->collection_data_params_mapping, true )
) {
$context['collectionData'] = $this->current_response;
Expand All @@ -148,28 +148,27 @@ public function modify_inner_blocks_context( $context, $parsed_block, $parent_bl
* @return array
*/
private function get_aggregated_collection_data( $block ) {
$collection_data_params = array();
$inner_blocks = array();
$inner_blocks = $this->get_inner_blocks_recursive( $block->inner_blocks );

do {
$inner_blocks = array_merge(
$this->get_inner_blocks_recursive( $block->inner_blocks->current() ),
$inner_blocks
);
$block->inner_blocks->next();
} while ( $block->inner_blocks->valid() );

foreach ( $this->collection_data_params_mapping as $key => $block_name ) {
$collection_data_params[ $key ] = ( in_array( $block_name, $inner_blocks, true ) );
}
$collection_data_params = array_map(
function( $block_name ) use ( $inner_blocks ) {
return in_array( $block_name, $inner_blocks, true );
},
$this->collection_data_params_mapping
);

if ( empty( array_filter( $collection_data_params ) ) ) {
return array();
}

$products_params = $this->get_formatted_products_params( $block->context['query'] );

$response = Package::container()->get( Hydration::class )->get_rest_api_response_data(
add_query_arg(
$collection_data_params,
array_merge(
$products_params,
$collection_data_params,
),
'/wc/store/v1/products/collection-data'
)
);
Expand All @@ -184,18 +183,111 @@ private function get_aggregated_collection_data( $block ) {
/**
* Get all inner blocks recursively.
*
* @param WP_Block $block The block to get inner blocks from.
* @param array $results The results array.
* @param WP_Block_List $inner_blocks The block to get inner blocks from.
* @param array $results The results array.
*
* @return array
*/
private function get_inner_blocks_recursive( $block, $results = array() ) {
$results[] = $block->name;
if ( ! empty( $block->inner_blocks ) ) {
foreach ( $block->inner_blocks as $inner_block ) {
$results = $this->get_inner_blocks_recursive( $inner_block, $results );
private function get_inner_blocks_recursive( $inner_blocks, &$results = array() ) {
if ( is_a( $inner_blocks, 'WP_Block_List' ) ) {
foreach ( $inner_blocks as $inner_block ) {
$results[] = $inner_block->name;
$this->get_inner_blocks_recursive(
$inner_block->inner_blocks,
$results
);
}
}

return $results;
}
Comment on lines +191 to 203
Copy link
Member Author

@dinhtungdu dinhtungdu Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function can be refactored into this:

	private function get_inner_blocks_recursive( $inner_blocks, &$results = array() ) {
		if ( ! is_a( $inner_blocks, 'WP_Block_List' ) ) {
			return $results;
		}

		return array_reduce(
			iterator_to_array( $inner_blocks ),
			function( $acc, $inner_block ) {
				$acc[] = $inner_block->name;
				return $this->get_inner_blocks_recursive(
					$inner_block->inner_blocks,
					$acc
				);
			},
			$results
		);
	}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this looks better? 🤔 I kinda like the first version with the loop to be honest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the short circuit, I do, but iterator_to_array() only accepts arrays from PHP 8.2, so we need that short circuit, which kinda decreases the readability.

We can remove the short circuit to make the logic flow less divergent, but I don't really like the inline condition.

	private function get_inner_blocks_recursive( $inner_blocks, &$results = array() ) {
		return array_reduce(
			is_a( $inner_blocks, 'WP_Block_List' ) ? iterator_to_array( $inner_blocks ) : array(),
			function( $acc, $inner_block ) {
				$acc[] = $inner_block->name;
				return $this->get_inner_blocks_recursive(
					$inner_block->inner_blocks,
					$acc
				);
			},
			$results
		);
	}


/**
* Get formatted products params for ProductCollectionData route from the
* query context.
*
* @param array $query The query context.
* @return array
*/
private function get_formatted_products_params( $query ) {
$params = array();

if ( empty( $query['isProductCollectionBlock'] ) ) {
return $params;
}

/**
* The following params can be passed directly to Store API endpoints.
*/
$shared_params = array( 'exclude', 'offset', 'order', 'serach' );
array_walk(
$shared_params,
function( $key ) use ( $query, &$params ) {
$params[ $key ] = $query[ $key ] ?? '';
}
);

/**
* The following params just need to transform the key, their value can
* be passed as it is to the Store API.
*/
$mapped_params = array(
'orderBy' => 'orderby',
'pages' => 'page',
'parents' => 'parent',
'perPage' => 'per_page',
'woocommerceStockStatus' => 'stock_status',
'woocommerceOnSale' => 'on_sale',
'woocommerceHandPickedProducts' => 'include',
);
array_walk(
$mapped_params,
function( $mapped_key, $original_key ) use ( $query, &$params ) {
$params[ $mapped_key ] = $query[ $original_key ] ?? '';
}
);

/**
* The value of taxQuery and woocommerceAttributes need additional
* transformation to the shape that Store API accepts.
*/
$taxonomy_mapper = function( $key ) {
$mapping = array(
'product_tag' => 'tag',
'product_cat' => 'category',
);

return $mapping[ $key ] ?? '_unstable_tax_' . $key;
};

if ( is_array( $query['taxQuery'] ) ) {
array_walk(
$query['taxQuery'],
function( $terms, $taxonomy ) use ( $taxonomy_mapper, &$params ) {
$params[ $taxonomy_mapper( $taxonomy ) ] = implode( ',', $terms );
}
);
}

if ( is_array( $query['woocommerceAttributes'] ) ) {
array_walk(
$query['woocommerceAttributes'],
function( $attribute ) use ( &$params ) {
$params['attributes'][] = array(
'attribute' => $attribute['taxonomy'],
'term_id' => $attribute['termId'],
);
}
);
}

/**
* Product Collection determines the product visibility based on stock
* statuses. We need to pass the catalog_visibility param to the Store
* API to make sure the product visibility is correct.
*/
$params['catalog_visibility'] = is_search() ? 'catalog' : 'visible';

return array_filter( $params );
}
}
Loading