WC_Product_Data_Store_CPT::find_matching_product_variation()publicWC 3.0.0

Find a matching (enabled) variation within a variable product.

Method of the class: WC_Product_Data_Store_CPT{}

No Hooks.

Return

Int. Matching variation ID or 0.

Usage

$WC_Product_Data_Store_CPT = new WC_Product_Data_Store_CPT();
$WC_Product_Data_Store_CPT->find_matching_product_variation( $product, $match_attributes );
$product(WC_Product) (required)
Variable product.
$match_attributes(array)
Array of attributes we want to try to match.
Default: array()

Changelog

Since 3.0.0 Introduced.

WC_Product_Data_Store_CPT::find_matching_product_variation() code WC 9.3.3

public function find_matching_product_variation( $product, $match_attributes = array() ) {
	if ( 'variation' === $product->get_type() ) {
		// Can't get a variation of a variation.
		return 0;
	}

	global $wpdb;

	$meta_attribute_names = array();

	// Get attributes to match in meta.
	foreach ( $product->get_attributes() as $attribute ) {
		if ( ! $attribute->get_variation() ) {
			continue;
		}
		$meta_attribute_names[] = 'attribute_' . sanitize_title( $attribute->get_name() );
	}

	// Get the attributes of the variations.
	$query = $wpdb->prepare(
		"
		SELECT postmeta.post_id, postmeta.meta_key, postmeta.meta_value, posts.menu_order FROM {$wpdb->postmeta} as postmeta
		LEFT JOIN {$wpdb->posts} as posts ON postmeta.post_id=posts.ID
		WHERE postmeta.post_id IN (
			SELECT ID FROM {$wpdb->posts}
			WHERE {$wpdb->posts}.post_parent = %d
			AND {$wpdb->posts}.post_status = 'publish'
			AND {$wpdb->posts}.post_type = 'product_variation'
		)
		",
		$product->get_id()
	);

	$query .= " AND postmeta.meta_key IN ( '" . implode( "','", array_map( 'esc_sql', $meta_attribute_names ) ) . "' )";

	$query .= ' ORDER BY posts.menu_order ASC, postmeta.post_id ASC;';

	$attributes = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

	if ( ! $attributes ) {
		return 0;
	}

	$sorted_meta = array();

	foreach ( $attributes as $m ) {
		$sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
	}

	/**
	 * Check each variation to find the one that matches the $match_attributes.
	 *
	 * Note: Not all meta fields will be set which is why we check existence.
	 */
	foreach ( $sorted_meta as $variation_id => $variation ) {
		$match = true;

		// Loop over the variation meta keys and values i.e. what is saved to the products. Note: $attribute_value is empty when 'any' is in use.
		foreach ( $variation as $attribute_key => $attribute_value ) {
			$match_any_value = '' === $attribute_value;

			if ( ! $match_any_value && ! array_key_exists( $attribute_key, $match_attributes ) ) {
				$match = false; // Requires a selection but no value was provide.
			}

			if ( array_key_exists( $attribute_key, $match_attributes ) ) { // Value to match was provided.
				if ( ! $match_any_value && $match_attributes[ $attribute_key ] !== $attribute_value ) {
					$match = false; // Provided value does not match variation.
				}
			}
		}

		if ( true === $match ) {
			return $variation_id;
		}
	}

	if ( version_compare( get_post_meta( $product->get_id(), '_product_version', true ), '2.4.0', '<' ) ) {
		/**
		 * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
		 * Fallback is here because there are cases where data will be 'synced' but the product version will remain the same.
		 */
		return ( array_map( 'sanitize_title', $match_attributes ) === $match_attributes ) ? 0 : $this->find_matching_product_variation( $product, array_map( 'sanitize_title', $match_attributes ) );
	}

	return 0;
}