r( 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 []; } }