}
return array(
'meta_query' => array(
array(
'relation' => 'AND',
$max_price_query,
$min_price_query,
),
),
);
}
/**
* Return a query that filters products by attributes.
*
* @return array
*/
private function get_filter_by_attributes_query() {
$attributes_filter_query_args = $this->get_filter_by_attributes_query_vars();
$queries = array_reduce(
$attributes_filter_query_args,
function( $acc, $query_args ) {
$attribute_name = $query_args['filter'];
$attribute_query_type = $query_args['query_type'];
$attribute_value = get_query_var( $attribute_name );
$attribute_query = get_query_var( $attribute_query_type );
if ( empty( $attribute_value ) ) {
return $acc;
}
// It is necessary explode the value because $attribute_value can be a string with multiple values (e.g. "red,blue").
$attribute_value = explode( ',', $attribute_value );
$acc[] = array(
'taxonomy' => str_replace( AttributeFilter::FILTER_QUERY_VAR_PREFIX, 'pa_', $attribute_name ),
'field' => 'slug',
'terms' => $attribute_value,
'operator' => 'and' === $attribute_query ? 'AND' : 'IN',
);
return $acc;
},
array()
);
if ( empty( $queries ) ) {
return array();
}
return array(
'tax_query' => array(
array(
'relation' => 'AND',
$queries,
),
),
);
}
/**
* Return a query that filters products by stock status.
*
* @return array
*/
private function get_filter_by_stock_status_query() {
$filter_stock_status_values = get_query_var( StockFilter::STOCK_STATUS_QUERY_VAR );
if ( empty( $filter_stock_status_values ) ) {
return array();
}
$filtered_stock_status_values = array_filter(
explode( ',', $filter_stock_status_values ),
function( $stock_status ) {
return in_array( $stock_status, StockFilter::get_stock_status_query_var_values(), true );
}
);
if ( empty( $filtered_stock_status_values ) ) {
return array();
}
return array(
// Ignoring the warning of not using meta queries.
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
array(
'key' => '_stock_status',
'value' => $filtered_stock_status_values,
'operator' => 'IN',
),
),
);
}
/**
* Return or initialize $valid_query_vars.
*
* @return array
*/
private function get_valid_query_vars() {
if ( ! empty( $this->valid_query_vars ) ) {
return $this->valid_query_vars;
}
$valid_query_vars = array_keys( ( new WP_Query() )->fill_query_vars( array() ) );
$this->valid_query_vars = array_merge(
$valid_query_vars,
// fill_query_vars doesn't include these vars so we need to add them manually.
array(
'date_query',
'exact',
'ignore_sticky_posts',
'lazy_load_term_meta',
'meta_compare_key',
'meta_compare',
'meta_query',
'meta_type_key',
'meta_type',
'nopaging',
'offset',
'order',
'orderby',
'page',
'post_type',
'posts_per_page',
'suppress_filters',
'tax_query',
)
);
return $this->valid_query_vars;
}
/**
* Merge two array recursively but replace the non-array values instead of
* merging them. The merging strategy:
*
* - If keys from merge array doesn't exist in the base array, create them.
* - For array items with numeric keys, we merge them as normal.
* - For array items with string keys:
*
* - If the value isn't array, we'll use the value comming from the merge array.
* $base = ['orderby' => 'date']
* $new = ['orderby' => 'meta_value_num']
* Result: ['orderby' => 'meta_value_num']
*
* - If the value is array, we'll use recursion to merge each key.
* $base = ['meta_query' => [
* [
* 'key' => '_stock_status',
* 'compare' => 'IN'
* 'value' => ['instock', 'onbackorder']
* ]
* ]]
* $new = ['meta_query' => [
* [
* 'relation' => 'AND',
* [...],
* [...],
* ]
* ]]
* Result: ['meta_query' => [
* [
* 'key' => '_stock_status',
* 'compare' => 'IN'
* 'value' => ['instock', 'onbackorder']
* ],
* [
* 'relation' => 'AND',
* [...],
* [...],
* ]
* ]]
*
* $base = ['post__in' => [1, 2, 3, 4, 5]]
* $new = ['post__in' => [3, 4, 5, 6, 7]]
* Result: ['post__in' => [1, 2, 3, 4, 5, 3, 4, 5, 6, 7]]
*
* @param array $base First array.
* @param array $new Second array.
*/
private function array_merge_recursive_replace_non_array_properties( $base, $new ) {
foreach ( $new as $key => $value ) {
if ( is_numeric( $key ) ) {
$base[] = $value;
} else {
if ( is_array( $value ) ) {
if ( ! isset( $base[ $key ] ) ) {
$base[ $key ] = array();
}
$base[ $key ] = $this->array_merge_recursive_replace_non_array_properties( $base[ $key ], $value );
} else {
$base[ $key ] = $value;
}
}
}
return $base;
}
/**
* Get product-related query variables from the global query.
*
* @param array $parsed_block The Product Query that being rendered.
*
* @return array
*/
private function get_global_query( $parsed_block ) {
if ( ! $this->is_custom_inherit_global_query_implementation_enabled ) {
return array();
}
global $wp_query;
$inherit_enabled = isset( $parsed_block['attrs']['query']['__woocommerceInherit'] ) && true === $parsed_block['attrs']['query']['__woocommerceInherit'];
if ( ! $inherit_enabled ) {
return array();
}
$query = array();
if ( isset( $wp_query->query_vars['taxonomy'] ) && isset( $wp_query->query_vars['term'] ) ) {
$query['tax_query'] = array(
array(
'taxonomy' => $wp_query->query_vars['taxonomy'],
'field' => 'slug',
'terms' => $wp_query->query_vars['term'],
),
);
}
if ( isset( $wp_query->query_vars['s'] ) ) {
$query['s'] = $wp_query->query_vars['s'];
}
return $query;
}
/**
* Return a query that filters products by rating.
*
* @return array
*/
private function get_filter_by_rating_query() {
$filter_rating_values = get_query_var( RatingFilter::RATING_QUERY_VAR );
if ( empty( $filter_rating_values ) ) {
return array();
}
$parsed_filter_rating_values = explode( ',', $filter_rating_values );
$product_visibility_terms = wc_get_product_visibility_term_ids();
if ( empty( $parsed_filter_rating_values ) || empty( $product_visibility_terms ) ) {
return array();
}
$rating_terms = array_map(
function( $rating ) use ( $product_visibility_terms ) {
return $product_visibility_terms[ 'rated-' . $rating ];
},
$parsed_filter_rating_values
);
return array(
'tax_query' => array(
array(
'field' => 'term_taxonomy_id',
'taxonomy' => 'product_visibility',
'terms' => $rating_terms,
'operator' => 'IN',
'rating_filter' => true,
),
),
);
}
/**
* Return a query to filter products by taxonomies (product categories, product tags, etc.)
*
* For example:
* User could provide "Product Categories" using "Filters" ToolsPanel available in Inspector Controls.
* We use this function to extract it's query from $tax_query.
*
* For example, this is how the query for product categories will look like in $tax_query array:
* Array
* (
* [taxonomy] => product_cat
* [terms] => Array
* (
* [0] => 36
* )
* )
*
* For product categories, taxonomy would be "product_tag"
*
* @param array $query WP_Query.
* @return array Query to filter products by taxonomies.
*/
private function get_filter_by_taxonomies_query( $query ): array {
if ( ! isset( $query['tax_query'] ) || ! is_array( $query['tax_query'] ) ) {
return [];
}
$tax_query = $query['tax_query'];
/**
* Get an array of taxonomy names associated with the "product" post type because
* we also want to include custom taxonomies associated with the "product" post type.
*/
$product_taxonomies = get_taxonomies( array( 'object_type' => array( 'product' ) ), 'names' );
$result = array_filter(
$tax_query,
function( $item ) use ( $product_taxonomies ) {
return isset( $item['taxonomy'] ) && in_array( $item['taxonomy'], $product_taxonomies, true );
}
);
return ! empty( $result ) ? [ 'tax_query' => $result ] : [];
}
/**
* Returns the keyword filter from the given query.
*
* @param WP_Query $query The query to extract the keyword filter from.
* @return array The keyword filter, or an empty array if none is found.
*/
private function get_filter_by_keyword_query( $query ): array {
if ( ! is_array( $query ) ) {
return [];
}
if ( isset( $query['s'] ) ) {
return [ 's' => $query['s'] ];
}
return [];
}
}