Automattic\WooCommerce\Internal\ProductFilters

QueryClauses::add_attribute_clausespublicWC 1.0

Add query clauses for filtering products by attributes.

Method of the class: QueryClauses{}

No Hooks.

Returns

Array.

Usage

$QueryClauses = new QueryClauses();
$QueryClauses->add_attribute_clauses( $args, $chosen_attributes ): array;
$args(array) (required)
Query args.
$chosen_attributes(array) (required)

Chosen attributes array.

  • {$taxonomy:(array)
    Attribute taxonomy name} {
    @type string[] $terms      Chosen terms' slug.
    @type string   $query_type Query type. Accepts 'and' or 'or'.

QueryClauses::add_attribute_clauses() code WC 10.3.3

public function add_attribute_clauses( array $args, array $chosen_attributes ): array {
	if ( empty( $chosen_attributes ) ) {
		return $args;
	}

	global $wpdb;

	// The extra derived table ("SELECT product_or_parent_id FROM") is needed for performance
	// (causes the filtering subquery to be executed only once).
	$clause_root = " {$wpdb->posts}.ID IN ( SELECT product_or_parent_id FROM (";
	if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
		$in_stock_clause = ' AND in_stock = 1';
	} else {
		$in_stock_clause = '';
	}

	$attribute_ids_for_and_filtering = array();
	$clauses                         = array();

	// Get all terms for all attribute taxonomies in one query for better performance.
	$all_terms_slugs = array();
	foreach ( $chosen_attributes as $data ) {
		if ( ! empty( $data['terms'] ) && is_array( $data['terms'] ) ) {
			$all_terms_slugs = array_merge( $all_terms_slugs, $data['terms'] );
		}
	}

	$all_terms = get_terms(
		array(
			'taxonomy'   => array_keys( $chosen_attributes ),
			'slug'       => $all_terms_slugs,
			'hide_empty' => false,
		)
	);

	if ( is_wp_error( $all_terms ) ) {
		return $args;
	}

	// Group terms by taxonomy for easier processing.
	$terms_by_taxonomy = array();
	foreach ( $all_terms as $term ) {
		$terms_by_taxonomy[ $term->taxonomy ][] = $term;
	}

	foreach ( $chosen_attributes as $taxonomy => $data ) {
		$current_attribute_terms    = $terms_by_taxonomy[ $taxonomy ] ?? array();
		$term_ids_by_slug           = wp_list_pluck( $current_attribute_terms, 'term_id', 'slug' );
		$term_ids_to_filter_by      = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) );
		$term_ids_to_filter_by      = array_map( 'absint', $term_ids_to_filter_by );
		$term_ids_to_filter_by_list = '(' . join( ',', $term_ids_to_filter_by ) . ')';
		$is_and_query               = 'and' === strtolower( $data['query_type'] );

		$count = count( $term_ids_to_filter_by );

		if ( 0 !== $count ) {
			if ( $is_and_query && $count > 1 ) {
				$attribute_ids_for_and_filtering = array_merge( $attribute_ids_for_and_filtering, $term_ids_to_filter_by );
			} else {
				$clauses[] = "
						{$clause_root}
						SELECT product_or_parent_id
						FROM {$this->get_lookup_table_name()} lt
						WHERE term_id in {$term_ids_to_filter_by_list}
						{$in_stock_clause}
					)";
			}
		}
	}

	if ( ! empty( $attribute_ids_for_and_filtering ) ) {
		$count                      = count( $attribute_ids_for_and_filtering );
		$term_ids_to_filter_by_list = '(' . join( ',', $attribute_ids_for_and_filtering ) . ')';
		$clauses[]                  = "
			{$clause_root}
			SELECT product_or_parent_id
			FROM {$this->get_lookup_table_name()} lt
			WHERE is_variation_attribute=0
			{$in_stock_clause}
			AND term_id in {$term_ids_to_filter_by_list}
			GROUP BY product_id
			HAVING COUNT(product_id)={$count}
			UNION
			SELECT product_or_parent_id
			FROM {$this->get_lookup_table_name()} lt
			WHERE is_variation_attribute=1
			{$in_stock_clause}
			AND term_id in {$term_ids_to_filter_by_list}
		)";
	}

	if ( ! empty( $clauses ) ) {
		// "temp" is needed because the extra derived tables require an alias.
		$args['where'] .= ' AND (' . join( ' temp ) AND ', $clauses ) . ' temp ))';
	} elseif ( ! empty( $chosen_attributes ) ) {
		$args['where'] .= ' AND 1=0';
	}

	return $args;
}