omukiguy
omukiguy

Reputation: 1437

Get the Product Variation related to default attribute values in WooCommerce

I would like to display the default product attributes form value and it's regular price in my front end template file.

The var_dump below shows options in an array. I Need to get the [default_attributes] values.

<?php 
    global $product;
    echo var_dump( $product );
// Need to get the [default_attributes] values
?>

enter image description here

Upvotes: 10

Views: 14868

Answers (5)

Mahdi Akrami
Mahdi Akrami

Reputation: 643

<?php

/**
 * Get default product variation
 *
 * note: this method check for exact match.
 *
 * @param mixed  $product_id WC_Product|WP_Post|int|bool $product Product instance, post instance, numeric or false to use global $post.
 * @param string $return Optional. The format to return the results in. Can be 'id' to return id of variation or 'object' for the product variation object. Default 'id'.
 *
 * @return int|WC_Product_Variation return 0 if not found.
 */
function wc_get_default_variation( $product_id = false, $return = 'id' ) {
    // do not use wc_get_product() to bypass some limits
    $product = WC()->product_factory->get_product( $product_id );

    if ( empty( $product ) || ! $product instanceof WC_Product_Variable ) {
        return 0;
    }

    if ( $product->has_child() ) {
        $attributes = $product->get_default_attributes();

        if ( ! empty( $attributes ) ) {
            $check_count      = true;
            $attributes_count = count( $attributes );

            // get in-stock (if enabled in wc options) and visible variations
            $variations = $product->get_available_variations( 'objects' );

            foreach ( $variations as $variation ) {
                $variation_attributes = $variation->get_attributes();

                // check count for first time
                // if not match, it means that user do not set default value for some variation attrs
                if ( $check_count && $attributes_count !== count( $variation_attributes ) ) {
                    break;
                }

                // no need to check count anymore
                $check_count = false;

                // remove 'any' attrs (empty values)
                $variation_attributes = array_filter( $variation_attributes );

                // add 'any' attrs with default value
                $variation_attributes = wp_parse_args( $variation_attributes, $attributes );

                // check is default
                if ( $variation_attributes == $attributes ) {
                    if ( $return === 'id' ) {
                        return $variation->get_id();
                    }

                    return $variation;
                }
            }
        }
    }

    return 0;
}

https://gist.github.com/AkramiPro/c8ddb83d253714730ac26d3a64e7941e

Upvotes: 0

Reagan
Reagan

Reputation: 2395

You can also use get_matching_variation function.

  function getDefaultVariation($product) {
    $defaultAttributes = $product->get_default_attributes();
  
    $formattedAttributes = [];

    foreach ($defaultAttributes as $taxonomy => $value) {
      $formattedAttributes["attribute_{$taxonomy}"] = $value;
    }

    $variationID = $product->get_matching_variation($formattedAttributes);

    // Output the default variation ID
    print_r($variationID);
  }

To get the full data of the default selected variation, you can call the WC_Product_Variation class and pass the variation ID. Here is the full implementation.

  function getDefaultVariation($product) {
    $defaultAttributes = $product->get_default_attributes();
  
    $formattedAttributes = [];

    foreach ($defaultAttributes as $taxonomy => $value) {
      $formattedAttributes["attribute_{$taxonomy}"] = $value;
    }

    $variationID = $product->get_matching_variation($formattedAttributes);

    if(!$variationID) {
      return;
    }
    $variation = new WC_Product_Variation($variationID);

    // Output the full data of the given variationID
    print_r($variation);
  }

Upvotes: 0

ZalemCitizen
ZalemCitizen

Reputation: 554

WC_Product_Data_Store_Interface offers a way to get a product variation matching a list of attributes for a variable WC_Product with ->find_matching_product_variation() :

function get_product_default_variation( $WC_Product ) {
  $default_attributes = $WC_Product->get_default_attributes();

  // ->find_matching_product_variation() needs term slugs of matching
  // attributes array to be prefixed with 'attribute_'
  $prefixed_slugs = array_map( function( $pa_name ) {
    return 'attribute_'. $pa_name;
  }, array_keys( $default_attributes ) );

  $default_attributes = array_combine( $prefixed_slugs, $default_attributes );

  $default_variation_id = ( new \WC_Product_Data_Store_CPT() )->find_matching_product_variation( $WC_Product, $default_attributes );

  return wc_get_product( $default_variation_id );
}

Upvotes: 2

VoidZA
VoidZA

Reputation: 197

I found this question while looking for a simpler way to do it than I was, perhaps a built in WooCommerce function, but for now we still need to find it ourselves.

I did this a little bit differently, and by using microtime to test its performance on my hardware, it runs in about 1/2000 of the time. Here is my code:

function setDefaultVariation(): array {
    global $product;
    $all_variations = $product->get_available_variations();
    $default_attributes = $product->get_default_attributes();
    if (empty($default_attributes))
        return $all_variations[0];

    $default_variation_key = -1;
    foreach ($all_variations as $variation) {
        $default_variation_key++;
        //Check if variation has more or less assigned attributes than the default.
        if (getAssignedAttributeCount($variation['attributes']) !== count($default_attributes))
            continue;

        $is_default = 0; //This will count how many attributes match the default values
        //We check each default attribute term and value to see if it matches the term-value pairs of the current variation. Some might have multiple pairs, so we use a counter to know if all are matched
        foreach ($default_attributes as $default_term => $default_value) {
            if($variation['attributes']['attribute_' . $default_term] !== $default_value) {
                break; //A attribute value does not match so this one cant be default, break.
            }else{
                $is_default++;
                if ($is_default === count($default_attributes) ) { //All default attributes matches this variation
                    return $all_variations[$default_variation_key];
                }
            }
        }
    }
    return $all_variations[0];//If this statement is reached, no default variation was found, so return key of first variation
}

function getAssignedAttributeCount($attributes): int {
    $count = 0;
    foreach ($attributes as $attribute) {
        if ($attribute !== '')
            $count++;
    }
    return $count;
}

The code finds the default attributes for a product, then loops through each variation to check if the variation's attributes matches the default attributes.

It supports products that have multiple attributes, and also checks that the variation has the exact attributes by comparing the number of attributes, so for example (NULL is a where a default value for an attribute is not set):

Default
|0|NULL|
Variation 1:
|0|2| -> The 0 matches, but the 2 does not, so this is not default!
Variation 2:
|0|NULL| -> This is the default

To achieve this, I added the getAssignedAttributeCount() function.

Also note that if no default variation is set, or no variation matching the default values is found, then the first variation is returned.

Upvotes: 0

LoicTheAztec
LoicTheAztec

Reputation: 254492

To get the default attributes for a variable product you can use WC_Product method get_default_attributes() this way:

<?php 
    global $product;

    if( $product->is_type('variable') ){
        $default_attributes = $product->get_default_attributes();

        // Testing raw output
        var_dump($default_attributes);
    }
?>

Now to find out which is the corresponding product variation for this "defaults" attributes, is a little more complicated:

<?php 
    global $product;

    if( $product->is_type('variable') ){
        $default_attributes = $product->get_default_attributes();
        foreach($product->get_available_variations() as $variation_values ){
            foreach($variation_values['attributes'] as $key => $attribute_value ){
                $attribute_name = str_replace( 'attribute_', '', $key );
                $default_value = $product->get_variation_default_attribute($attribute_name);
                if( $default_value == $attribute_value ){
                    $is_default_variation = true;
                } else {
                    $is_default_variation = false;
                    break; // Stop this loop to start next main lopp
                }
            }
            if( $is_default_variation ){
                $variation_id = $variation_values['variation_id'];
                break; // Stop the main loop
            }
        }

        // Now we get the default variation data
        if( $is_default_variation ){
            // Raw output of available "default" variation details data
            echo '<pre>'; print_r($variation_values); echo '</pre>';

            // Get the "default" WC_Product_Variation object to use available methods
            $default_variation = wc_get_product($variation_id);

            // Get The active price
            $price = $default_variation->get_price(); 
        }
    }
?>

This is tested and works.

Upvotes: 18

Related Questions