Get the selected variation attributes from orders in Woocommerce 3
Asked Answered
M

4

6

In Woocommerce, I have a report in the admin area that tallies up products sold.There are only 5 products sold on the site but there are 1 or 2 variations on some. The report works great but ignores the variations.

I need to retrieve the attribute value from items ordered to display the data accurately. How do I do this?

get_variation_description() is not working the way I'm applying it.

My code:

$order = wc_get_order( $vs); 
                
//BEGIN NEW RETRIEVE ORDER ITEMS FROM ORDER 
foreach( $order->get_items() as $item_id => $item_product ){
    $ods = $item_product->get_product_id(); //Get the product ID
    $odqty= $item_product->get_quantity(); //Get the product QTY
    $item_name = $item_product->get_name(); //Get the product NAME
    $item_variation = $item_product->get_variation_description(); //NOT WORKING
}
Motorist answered 22/1, 2018 at 21:50 Comment(0)
E
8

2020 Update - Handling "Custom Product Attributes" (revamped code)

The WC_Product method get_variation_description() is outdated and deprecated. It's replaced by get_description() method. So you need to get the WC_Product object first.

To get the selected variation attributes, you will use get_variation_attributes( ) method.

// Get an instance of the WC_Order object from an Order ID
 $order = wc_get_order( $order_id ); 

// Loop though order "line items"
foreach( $order->get_items() as $item_id => $item ){
    $product_id   = $item->get_product_id(); //Get the product ID
    $quantity     = $item->get_quantity(); //Get the product QTY
    $product_name = $item->get_name(); //Get the product NAME

     // Get an instance of the WC_Product object (can be a product variation  too)
    $product      = $item->get_product();

     // Get the product description (works for product variation too)
    $description  = $product->get_description();

    // Only for product variation
    if( $product->is_type('variation') ){
         // Get the variation attributes
        $variation_attributes = $product->get_variation_attributes();
        // Loop through each selected attributes
        foreach($variation_attributes as $attribute_taxonomy => $term_slug ){
            // Get product attribute name or taxonomy
            $taxonomy = str_replace('attribute_', '', $attribute_taxonomy );
            // The label name from the product attribute
            $attribute_name = wc_attribute_label( $taxonomy, $product );
            // The term name (or value) from this attribute
            if( taxonomy_exists($taxonomy) ) {
                $attribute_value = get_term_by( 'slug', $term_slug, $taxonomy )->name;
            } else {
                $attribute_value = $term_slug; // For custom product attributes
            }
        }
    }
}

Tested and works for a product variation as all other product types…

Eu answered 22/1, 2018 at 22:48 Comment(9)
Thx, but I am unable to retrieve the attribute value selected by the customer this way. When you get to $variation_attributes and I do a var_dump on it, my output is array(1) { ["attribute_dish-options"]=> string(0) "" }. When you go through the loop both $attribute_name and $attribute_value are null. Is this because we are getting an instance of the WC_Product and not the WC_Order object?Motorist
Using your code helped me identify the attribute name taxonomy. I used that in the following code and it is returning the selected attribute value now. Is there a way to loop through the code below to return all selected attribute values (if there is more than one variation per product) without having to name each attribute name in the code? $dish_options = wc_get_order_item_meta( $item_id, "dish-options", true ); This is from one of your previous answers at #44986368Motorist
@Motorist This answer, is just answers your initial question. My code is tested and works in WC 3+ for all kind of product variations, with one or multiple attributes/values pairs (custom or not). You don't need to name anything, you just have to take and manipulate the data that you need… Comments are not very practical and I don't really catch what you want above. If you have another question, you should better ask a new question (and notify me here, if you like). Thanks.Eu
Ok I'll test it again and see if I can tell why I'm not getting the expected results.Motorist
@Motorist +1 for wc_get_order_item_meta(), this is also what I was looking for.Longsome
This doesn't work if someone has added the attributes using "Custom Product Attributes", is there a way for it to include that too?Beckmann
@Beckmann In don't catch… What is "Custom Product Attributes"?Eu
@Eu so when using your code above, it pulls in the information if the attributes are registered globally e.g. Products -> Attributes, but when creating products you can select Add Custom Product Attributes, this lets you select a title so "Type" and values separated by a pipe, so "Type", "Adhesive|Poster". When defined this way, it will not show. So what I see is "Colour: Red" then " : ", " : "Beckmann
@Beckmann Updated my answer code to handle "Custom Product Attributes"…Eu
D
4

It works perfectly to display order item name and attributes key

foreach( $order->get_items() as $order_item_product ) {
    $item_meta_data = $order_item_product->get_meta_data();
    echo $product_name = $order_item_product->get_name();
    echo '<br>';
    foreach( $item_meta_data as $meta_data ) {
        $meta_data_as_array = $meta_data->get_data();
        echo $meta_data_as_array['key'].': '.$meta_data_as_array['value'].'<br>';
    }
}
Dodgem answered 18/11, 2019 at 7:47 Comment(2)
solid, simple answer to get all order product meta dataMerocrine
This is the best answer!Voyeurism
W
1

Based on the accepted answer This is so that the typo could be corrected ( i don't have the reputation to do anything else). notice the $term_slug on the $attribute_value property definition. that is what was missing the $.

// Get an instance of the WC_Order object from an Order ID
    $order = wc_get_order( $order_id ); 

   // Loop though order "line items"
   foreach( $order->get_items() as $item_id => $item_product ){
      $product_id = $item_product->get_product_id(); //Get the product ID
      $quantity = $item_product->get_quantity(); //Get the product QTY
      $product_name = $item_product->get_name(); //Get the product NAME

      // Get an instance of the WC_Product object (can be a product variation  too)
      $product = $item_product->get_product();

      // Get the product description (works for product variation)
      $description = $product->get_description();

      // Only for product variation
      if($product->is_type('variation')){
         // Get the variation attributes
        $variation_attributes = $product->get_variation_attributes();
        // Loop through each selected attributes
        foreach($variation_attributes as $attribute_taxonomy => $term_slug){
            $taxonomy = str_replace('attribute_', '', $attribute_taxonomy );
            // The name of the attribute
            $attribute_name = get_taxonomy( $taxonomy )->labels->singular_name;
            // The term name (or value) for this attribute
            $attribute_value = get_term_by( 'slug', $term_slug, $taxonomy )->name;
        }
      }
   }
Waylen answered 19/9, 2018 at 18:38 Comment(1)
Great catch! Thank youMotorist
P
0

For anyone coming across this in the future, I ran into an issue where the last attribute value was not displaying, but when I was looking at orders in the admin it was displaying correctly, so I took a dive into the WooCommerce source code and modified theirs for my needs. You can see their code here if you want to modify it yourself:

\woocommerce\includes\admin\meta-boxes\views\html-order-item-meta.php

Here's what I did to display all keys and values:

$attribute_list = array();

// Start modified code from html-order-item-meta.php
$hidden_order_itemmeta = apply_filters(
    'woocommerce_hidden_order_itemmeta',
    array(
        '_qty',
        '_tax_class',
        '_product_id',
        '_variation_id',
        '_line_subtotal',
        '_line_subtotal_tax',
        '_line_total',
        '_line_tax',
        'method_id',
        'cost',
        '_reduced_stock',
        '_restock_refunded_items',
    )
);

if ($meta_data = $item->get_formatted_meta_data('')) {
    foreach ($meta_data as $meta_id => $meta) {
        if (in_array($meta->key, $hidden_order_itemmeta, true)) {
            continue;
        }
        $display_key = sanitize_text_field($meta->display_key);
        $display_value = sanitize_text_field(force_balance_tags($meta->display_value));

        $attribute_list[] = array($display_key => $display_value);
    }
}
// End modified code from html-order-item-meta.php

if ($attribute_list) {
    $attribute = '';

    foreach ($attribute_list as $attributes) {
        foreach ($attributes as $attr => $value) {
            $attribute .= " - " . $attr . " : " . $value;
        }
    }

    echo $attribute;
}

My code adds each key and value to an array and then loops through to append the key and value to the end of a string with separators. This is what I needed for what I was working on, but it can be easily adapted to fit your needs.

Penton answered 14/6, 2022 at 22:0 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.