ProductFilterPrice.php
<?php
declare( strict_types = 1 );
namespace Automattic\WooCommerce\Blocks\BlockTypes;
use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Utils as ProductCollectionUtils;
use Automattic\WooCommerce\Internal\ProductFilters\FilterDataProvider;
use Automattic\WooCommerce\Internal\ProductFilters\QueryClauses;
/**
* Product Filter: Price Block.
*/
final class ProductFilterPrice extends AbstractBlock {
use EnableBlockJsonAssetsTrait;
/**
* Block name.
*
* @var string
*/
protected $block_name = 'product-filter-price';
const MIN_PRICE_QUERY_VAR = 'min_price';
const MAX_PRICE_QUERY_VAR = 'max_price';
/**
* Initialize this block type.
*
* - Hook into WP lifecycle.
* - Register the block with WordPress.
*/
protected function initialize() {
parent::initialize();
add_filter( 'woocommerce_blocks_product_filters_param_keys', array( $this, 'get_filter_query_param_keys' ), 10, 2 );
add_filter( 'woocommerce_blocks_product_filters_selected_items', array( $this, 'prepare_selected_filters' ), 10, 2 );
}
/**
* Prepare the active filter items.
*
* @param array $items The active filter items.
* @param array $params The query param parsed from the URL.
* @return array Active filters items.
*/
public function prepare_selected_filters( $items, $params ) {
$min_price = intval( $params[ self::MIN_PRICE_QUERY_VAR ] ?? 0 );
$max_price = intval( $params[ self::MAX_PRICE_QUERY_VAR ] ?? 0 );
$formatted_min_price = $min_price ? html_entity_decode( wp_strip_all_tags( wc_price( $min_price, array( 'decimals' => 0 ) ) ) ) : null;
$formatted_max_price = $max_price ? html_entity_decode( wp_strip_all_tags( wc_price( $max_price, array( 'decimals' => 0 ) ) ) ) : null;
if ( ! $formatted_min_price && ! $formatted_max_price ) {
return $items;
}
$item = array(
'type' => 'price',
);
if ( $formatted_min_price && $formatted_max_price ) {
$item['activeLabel'] = sprintf(
/* translators: %1$s and %2$s are the formatted minimum and maximum prices respectively. */
__( 'Price: %1$s - %2$s', 'woocommerce' ),
$formatted_min_price,
$formatted_max_price
);
$item['value'] = "{$min_price}|{$max_price}";
}
if ( ! $formatted_min_price ) {
/* translators: %s is the formatted maximum price. */
$item['activeLabel'] = sprintf( __( 'Price: Up to %s', 'woocommerce' ), $formatted_max_price );
$item['value'] = "|{$max_price}";
}
if ( ! $formatted_max_price ) {
/* translators: %s is the formatted minimum price. */
$item['activeLabel'] = sprintf( __( 'Price: From %s', 'woocommerce' ), $formatted_min_price );
$item['value'] = "{$min_price}|";
}
$items[] = $item;
return $items;
}
/**
* Register the query param keys.
*
* @param array $filter_param_keys The active filters data.
* @param array $url_param_keys The query param parsed from the URL.
*
* @return array Active filters param keys.
*/
public function get_filter_query_param_keys( $filter_param_keys, $url_param_keys ) {
$price_param_keys = array_filter(
$url_param_keys,
function ( $param ) {
return self::MIN_PRICE_QUERY_VAR === $param || self::MAX_PRICE_QUERY_VAR === $param;
}
);
return array_merge(
$filter_param_keys,
$price_param_keys
);
}
/**
* Render the block.
*
* @param array $attributes Block attributes.
* @param string $content Block content.
* @param WP_Block $block Block instance.
* @return string Rendered block type output.
*/
protected function render( $attributes, $content, $block ) {
// don't render if its admin, or ajax in progress.
if ( is_admin() || wp_doing_ajax() ) {
return '';
}
$price_range = $this->get_filtered_price( $block );
$min_range = $price_range['min_price'] ?? 0;
$max_range = $price_range['max_price'] ?? 0;
$filter_params = $block->context['filterParams'] ?? array();
$min_price = intval( $filter_params[ self::MIN_PRICE_QUERY_VAR ] ?? $min_range );
$max_price = intval( $filter_params[ self::MAX_PRICE_QUERY_VAR ] ?? $max_range );
$formatted_min_price = html_entity_decode( wp_strip_all_tags( wc_price( $min_price, array( 'decimals' => 0 ) ) ) );
$formatted_max_price = html_entity_decode( wp_strip_all_tags( wc_price( $max_price, array( 'decimals' => 0 ) ) ) );
$filter_context = array(
'price' => array(
'minPrice' => $min_price,
'maxPrice' => $max_price,
'minRange' => $min_range,
'maxRange' => $max_range,
),
'groupLabel' => __( 'Price', 'woocommerce' ),
);
$wrapper_attributes = array(
'data-wp-interactive' => 'woocommerce/product-filters',
'data-wp-key' => wp_unique_prefixed_id( $this->get_full_block_name() ),
'data-wp-context' => wp_json_encode(
array(
'filterType' => 'price',
'minRange' => $min_range,
'maxRange' => $max_range,
),
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP,
),
);
wp_interactivity_config(
'woocommerce/product-filters',
array(
'activePriceLabelTemplates' => array(
/* translators: {{min}} and {{max}} are the formatted minimum and maximum prices respectively. */
'minAndMax' => __( 'Price: {{min}} - {{max}}', 'woocommerce' ),
/* translators: {{max}} is the formatted maximum price. */
'maxOnly' => __( 'Price: Up to {{max}}', 'woocommerce' ),
/* translators: {{min}} is the formatted minimum price. */
'minOnly' => __( 'Price: From {{min}}', 'woocommerce' ),
),
)
);
wp_interactivity_state(
'woocommerce/product-filters',
array(
'formattedMinPrice' => $formatted_min_price,
'formattedMaxPrice' => $formatted_max_price,
'minPrice' => $min_price,
'maxPrice' => $max_price,
)
);
if ( $min_range === $max_range || ! $max_range ) {
$wrapper_attributes['hidden'] = true;
$wrapper_attributes['class'] = 'wc-block-product-filter--hidden';
return sprintf(
'<div %1$s>%2$s</div>',
get_block_wrapper_attributes( $wrapper_attributes ),
array_reduce(
$block->parsed_block['innerBlocks'],
function ( $carry, $parsed_block ) {
$carry .= render_block( $parsed_block );
return $carry;
},
''
)
);
}
return sprintf(
'<div %1$s>%2$s</div>',
get_block_wrapper_attributes( $wrapper_attributes ),
array_reduce(
$block->parsed_block['innerBlocks'],
function ( $carry, $parsed_block ) use ( $filter_context ) {
$carry .= ( new \WP_Block( $parsed_block, array( 'filterData' => $filter_context ) ) )->render();
return $carry;
},
''
)
);
}
/**
* Retrieve the price filter data for current block.
*
* @param WP_Block $block Block instance.
*/
private function get_filtered_price( $block ) {
$query_vars = ProductCollectionUtils::get_query_vars( $block, 1 );
unset( $query_vars['min_price'], $query_vars['max_price'] );
if ( ! empty( $query_vars['meta_query'] ) ) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
$query_vars['meta_query'] = ProductCollectionUtils::remove_query_array( $query_vars['meta_query'], 'key', '_price' );
}
if ( isset( $query_vars['taxonomy'] ) && false !== strpos( $query_vars['taxonomy'], 'pa_' ) ) {
unset(
$query_vars['taxonomy'],
$query_vars['term']
);
}
$container = wc_get_container();
$price_results = $container->get( FilterDataProvider::class )->with( $container->get( QueryClauses::class ) )->get_filtered_price( $query_vars );
return array(
'min_price' => intval( floor( floatval( $price_results['min_price'] ?? 0 ) ) ),
'max_price' => intval( ceil( floatval( $price_results['max_price'] ?? 0 ) ) ),
);
}
}