[Product Collection] Parse front-end context (#44145)
* Parse global context * Refactor the util to only parse global context, provide the location context to collection children * cleanup * Cleanup * Add changelog * Make linters happy * provide context to each inner block recursively * Fix linters * Remove debug * Update plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php Co-authored-by: Manish Menaria <the.manish.menaria@gmail.com> * Rename the provider method * Fix typo on cart item products * Use the query context instead * Lint * Lint again * Minor change on docs * Polish * Cleanup --------- Co-authored-by: Manish Menaria <the.manish.menaria@gmail.com>
This commit is contained in:
parent
be849473e0
commit
d3df046f97
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: enhancement
|
||||
|
||||
Provide the location context within the Product Collection block context
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\ProductCollectionUtils;
|
||||
use WP_Query;
|
||||
use WC_Tax;
|
||||
|
||||
|
@ -76,6 +77,9 @@ class ProductCollection extends AbstractBlock {
|
|||
// Extend allowed `collection_params` for the REST API.
|
||||
add_filter( 'rest_product_collection_params', array( $this, 'extend_rest_query_allowed_params' ), 10, 1 );
|
||||
|
||||
// Provide location context into block's context.
|
||||
add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 );
|
||||
|
||||
// Interactivity API: Add navigation directives to the product collection block.
|
||||
add_filter( 'render_block_woocommerce/product-collection', array( $this, 'enhance_product_collection_with_interactivity' ), 10, 2 );
|
||||
add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 );
|
||||
|
@ -86,6 +90,71 @@ class ProductCollection extends AbstractBlock {
|
|||
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the location context to each inner block of the product collection block.
|
||||
* Hint: Only blocks using the 'query' context will be affected.
|
||||
*
|
||||
* The sourceData structure depends on the context type as follows:
|
||||
* - site: [ ]
|
||||
* - order: [ 'orderId' => int ]
|
||||
* - cart: [ 'productIds' => int[] ]
|
||||
* - archive: [ 'taxonomy' => string, 'termId' => int ]
|
||||
* - product: [ 'productId' => int ]
|
||||
*
|
||||
* @example array(
|
||||
* 'type' => 'product',
|
||||
* 'sourceData' => array( 'productId' => 123 ),
|
||||
* )
|
||||
*
|
||||
* @param array $context The block context.
|
||||
* @return array $context {
|
||||
* The block context including the product collection location context.
|
||||
*
|
||||
* @type array $productCollectionLocation {
|
||||
* @type string $type The context type. Possible values are 'site', 'order', 'cart', 'archive', 'product'.
|
||||
* @type array $sourceData The context source data. Can be the product ID of the viewed product, the order ID of the current order viewed, etc. See structure above for more details.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function provide_location_context_for_inner_blocks( $context ) {
|
||||
// Run only on frontend.
|
||||
// This is needed to avoid SSR renders while in editor. @see https://github.com/woocommerce/woocommerce/issues/45181.
|
||||
if ( is_admin() || \WC()->is_rest_api_request() ) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
// Target only product collection's inner blocks that use the 'query' context.
|
||||
if ( ! isset( $context['query'] ) || ! isset( $context['query']['isProductCollectionBlock'] ) || ! $context['query']['isProductCollectionBlock'] ) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
$is_in_single_product = isset( $context['singleProduct'] ) && ! empty( $context['postId'] );
|
||||
$context['productCollectionLocation'] = $is_in_single_product ? array(
|
||||
'type' => 'product',
|
||||
'sourceData' => array(
|
||||
'productId' => absint( $context['postId'] ),
|
||||
),
|
||||
) : $this->get_location_context();
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global location context.
|
||||
* Serve as a runtime cache for the location context.
|
||||
*
|
||||
* @see ProductCollectionUtils::parse_frontend_location_context()
|
||||
*
|
||||
* @return array The location context.
|
||||
*/
|
||||
private function get_location_context() {
|
||||
static $location_context = null;
|
||||
if ( null === $location_context ) {
|
||||
$location_context = ProductCollectionUtils::parse_frontend_location_context();
|
||||
}
|
||||
return $location_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the Product Collection block with client-side pagination.
|
||||
*
|
||||
|
@ -451,6 +520,7 @@ class ProductCollection extends AbstractBlock {
|
|||
}
|
||||
|
||||
$block_context_query = $block->context['query'];
|
||||
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
$block_context_query['tax_query'] = ! empty( $query['tax_query'] ) ? $query['tax_query'] : array();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use WP_Query;
|
|||
* {@internal This class and its methods are not intended for public use.}
|
||||
*/
|
||||
class ProductCollectionUtils {
|
||||
|
||||
/**
|
||||
* Prepare and execute a query for the Product Collection block.
|
||||
* This method is used by the Product Collection block and the No Results block.
|
||||
|
@ -78,6 +79,87 @@ class ProductCollectionUtils {
|
|||
return self::remove_empty_array_recursive( $queries );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse WP Query's front-end context for the Product Collection block.
|
||||
*
|
||||
* The sourceData structure depends on the context type as follows:
|
||||
* - site: [ ]
|
||||
* - order: [ 'orderId' => int ]
|
||||
* - cart: [ 'productIds' => int[] ]
|
||||
* - archive: [ 'taxonomy' => string, 'termId' => int ]
|
||||
* - product: [ 'productId' => int ]
|
||||
*
|
||||
* @return array $context {
|
||||
* @type string $type The context type. Possible values are 'site', 'order', 'cart', 'archive', 'product'.
|
||||
* @type array $sourceData The context source data. Can be the product ID of the viewed product, the order ID of the current order, etc.
|
||||
* }
|
||||
*/
|
||||
public static function parse_frontend_location_context() {
|
||||
global $wp_query;
|
||||
|
||||
// Default context.
|
||||
// Hint: The Shop page uses the default context.
|
||||
$type = 'site';
|
||||
$source_data = array();
|
||||
|
||||
if ( ! ( $wp_query instanceof WP_Query ) ) {
|
||||
|
||||
return array(
|
||||
'type' => $type,
|
||||
'sourceData' => $source_data,
|
||||
);
|
||||
}
|
||||
|
||||
// As more areas are blockified, expected future contexts include:
|
||||
// - is_checkout_pay_page().
|
||||
// - is_view_order_page().
|
||||
if ( is_order_received_page() ) {
|
||||
|
||||
$type = 'order';
|
||||
$source_data = array( 'orderId' => absint( $wp_query->query_vars['order-received'] ) );
|
||||
|
||||
} elseif ( ( is_cart() || is_checkout() ) && isset( WC()->cart ) && is_a( WC()->cart, 'WC_Cart' ) ) {
|
||||
|
||||
$type = 'cart';
|
||||
$items = array();
|
||||
foreach ( WC()->cart->get_cart() as $cart_item ) {
|
||||
if ( ! isset( $cart_item['product_id'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items[] = absint( $cart_item['product_id'] );
|
||||
}
|
||||
$items = array_unique( array_filter( $items ) );
|
||||
$source_data = array( 'productIds' => $items );
|
||||
|
||||
} elseif ( is_product_taxonomy() ) {
|
||||
|
||||
$source = $wp_query->get_queried_object();
|
||||
$is_valid = is_a( $source, 'WP_Term' );
|
||||
$taxonomy = $is_valid ? $source->taxonomy : '';
|
||||
$term_id = $is_valid ? $source->term_id : '';
|
||||
$type = 'archive';
|
||||
$source_data = array(
|
||||
'taxonomy' => wc_clean( $taxonomy ),
|
||||
'termId' => absint( $term_id ),
|
||||
);
|
||||
|
||||
} elseif ( is_product() ) {
|
||||
|
||||
$source = $wp_query->get_queried_object();
|
||||
$product_id = is_a( $source, 'WP_Post' ) ? absint( $source->ID ) : 0;
|
||||
$type = 'product';
|
||||
$source_data = array( 'productId' => $product_id );
|
||||
}
|
||||
|
||||
$context = array(
|
||||
'type' => $type,
|
||||
'sourceData' => $source_data,
|
||||
);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove falsy item from array, recursively.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue