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
}
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…
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>';
}
}
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;
}
}
}
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.
Related
I have 2 attributions for color and size of t-shirt, I want to get the values that user selected after he creates the order
So when I check the data of the created order I don't find the related attribute of the product that the customer selected at all (btw I check through woocommerce_process_shop_order_meta hook )
function getcartitems($order_id)
$order = wc_get_order($order_id);
$order_data = $order->get_data(); // The Order data
$items = $order->get_items();
}
// the hook will be changed to woocommerce_checkout_order_processed
add_action('woocommerce_process_shop_order_meta', 'getcartitems');
I'm not sure what you want to do with the selected data, but here is how you wuld grab the values.
function getcartitems($order_id) {
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
// Get the items in the order
$items = $order->get_items();
foreach ( $items as $item ) {
// Get the product ID
$product_id = $item['product_id'];
// Get the product object
$product = wc_get_product( $product_id );
// Get the product attribute values
$attributes = $product->get_attributes();
foreach ( $attributes as $attribute ) {
// Get the attribute name
$attribute_name = $attribute['name'];
// Get the attribute value(s)
$attribute_values = $item['variation'][ "attribute_{$attribute['name']}" ];
// Output the attribute name and value(s)
echo "Attribute name: {$attribute_name} <br>";
echo "Attribute values: {$attribute_values} <br>";
}
}
}
// the hook will be changed to woocommerce_checkout_order_processed
add_action('woocommerce_process_shop_order_meta', 'getcartitems');
I should get all the data of the products in the cart (product names and attributes of individual products). As a reference I have this code that allows to display only one attribute per product.
Could you help me to find the solution to see all attributes?
In the store there are 10 attributes in total
$taxonomy = 'pa_selezione-pezzi';
// Get an instance of the WC_Product Object (necessary if you don't have it)
// from a dynamic $product_id (or defined $product_id)
$product = wc_get_product($product_id);
// Iterating through the product attributes
foreach($product->get_attributes() as $attribute){
// Targeting the defined attribute
if( $attribute->get_name() == $taxonomy ){
// Iterating through term IDs for this attribute (set for this product)
foreach($attribute->get_options() as $term_id){
// Get the term slug
$term_slug = get_term( $term_id, $taxonomy )->slug;
// Output
$confirmation = str_ireplace("{term_slug}", $term_slug, $confirmation);
}
}
}
If you are looking to retrieve the product attributes set in product variation cart items, you can use the following code example:
// loop through product attributes
foreach( WC()->cart->get_cart() as $item ) {
if( ! empty($item['variation']) ) {
// loop through product attributes
foreach($item['variation'] as $attribute => $term_name ) {
$taxonomy = str_replace('attribute_', '', $attribute);
$attribute_name = wc_attribute_label($taxonomy);
$term_name = get_term_by( 'slug, $term_name, $taxonomy)->name;
}
}
}
Tested and works
Thank you for your correct answer "mr LoicTheAztec";
It has developed your codes, for the use of friends;
Note:attributes $value label added:
//اضافه کردن نام ویژگی به اسم محصول
//Add attribute label name to product name
if( $product->is_type( 'variation' ) ){
$s ='';
$s1 = '';
foreach ($product->get_attributes() as $taxonomy => $attribute_obj ) {
// Get the attribute label
$attribute_label_name = wc_attribute_label($taxonomy);
//convert to array
$attribute_arr = json_decode(json_encode($attribute_obj),true);
$term_name = get_term_by( 'slug', $attribute_arr, $taxonomy)->name;
$s = $s . $s1 .$attribute_label_name.':'.$term_name;
$s1 = ', ';
}
$name = $name . '(' .$s. ')';
echo '<p>' . $name. '</p>';
}
I use following code to re save the product variations, when there is a paid order on it, but nothing happen
add_action('woocommerce_payment_complete', 'refresh_zero_stock');
function refresh_zero_stock($order_id){
$order = new WC_Order( $order_id );
foreach ($order->get_items() as $item_key => $item ){
$item_quantity = $item->get_quantity();
if($item_quantity == 0){
$product_id = $item->get_product_id();
$product_data = wc_get_product($product_id);
if ($product_data->is_type('variable')){
$handle = new WC_Product_Variable($product_id);
$variations1=$handle->get_children();
foreach ($variations1 as $value) {
$single_variation=new WC_Product_Variation($value);
$single_variation->save();
}
}
}
}
}
what is the problem with this action hook? please help.
The hook is not the problem… The main problem is if( $item->get_quantity() == 0 ){ that is always false, where $item->get_quantity() need to be replaced with the product stock quantity instead. Try the following:
add_action('woocommerce_payment_complete', 'refresh_zero_stock');
function refresh_zero_stock( $order_id ){
$order = wc_get_order( $order_id );
foreach ($order->get_items() as $item ){
$product = $item->get_product(); // Get the current product (object)
// If stock quantity is 0 and if it's a product variation
if ( $product->get_stock_quantity() == 0 && $product->is_type('variation') ){
$parent_product = wc_get_product( $item->get_product_id() ); // Get parent variable product
// Loop through children Ids (variations ids) from the parent variable product
foreach ($parent_product->get_children() as $child_id ) {
$variation = wc_get_product( $child_id ); // Get the product variation from each child Id
$variation->save(); // refresh and save
}
}
}
}
}
Code goes in functions.php file of the active child theme (or active theme). It should better works.
As the hook woocommerce_payment_complete doesn't seems to work, try the following instead based on order status change (Note: this code will run only once for each order):
add_action( 'woocommerce_order_status_changed', 'refresh_zero_stock', 10, 4 );
function refresh_zero_stock( $order_id, $from_status, $to_status, $order ){
$variations_refreshed = $order->get_meta('_variations_refreshed');
if( in_array($to_status, array('processing', 'completed') ) && ! $variations_refreshed ) {
// Loop though order items
foreach ($order->get_items() as $item ){
$product = $item->get_product(); // Get the current product (object)
// If stock quantity is 0 and if it's a product variation
if ( $product->get_stock_quantity() == 0 && $product->is_type('variation') ){
$parent_product = wc_get_product( $item->get_product_id() ); // Get parent variable product
// Loop through children Ids (variations ids) from the parent variable product
foreach ($parent_product->get_children() as $child_id ) {
$variation = wc_get_product( $child_id ); // Get the product variation from each child Id
$variation->save(); // refresh and save
}
}
}
}
// Flag the order to make this code run only once for current order
$order->update_meta_data( '_variations_refreshed', '1' );
}
}
Code goes in functions.php file of the active child theme (or active theme). It could better works.
I find the problem. I have to add
$variation->set_manage_stock(false);
$variation->set_stock_status('outofstock');
before
$variation->save();
In Woocommerce I have a Product Attribute called "Platform" the Value of the Attribute is "Steam":
So I am bulk importing the products and the Attributes are already there.
But now I have to set for every product manually the category.
Is it possible to set the Value automatically as Product Category in a function?
This Function is returning me the Attribute value right?
function get_attribute_value_from_name( $name ){
global $wpdb;
$name = 'Platform';
$attribute_value = $wpdb->get_var("SELECT attribute_value
FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
WHERE attribute_name LIKE '$name'");
return $attribute_value;
}
And now how to set the value for product category?
EDIT:
$product = wc_get_product($id); //LOAD PRODUCT
global $product_attribute; //VARIABLE
$product_attribute = $product->get_attribute( 'Platform' ); //GET ATTRIBUTE OF PLATFORM
wp_set_object_terms( $post_id, $product_attribute, 'product_cat' ); //WRITE IT AS CATEGORY
$product->save(); //SAVE PRODUCT
does this make sense?
Update 2 - To set an existing product category term in a product (using a defined product ID):
// Get an instance of the WC_Product object
$product = wc_get_product( $product_id );
$term_names = $product->get_attribute( 'Platform' ); // Can have many term names (coma separated)
$term_names = explode( ',', $term_names);
$term_ids = [];
// Loop through the terms
foreach( $term_names as $term_name ) {
// Get the term ID and check if it exist
if( $term_id = term_exists( $term_name, 'product_cat' ) ) {
// Add each term ID in an array
$term_ids[] = $term_id;
}
}
// Append the product category terms in the product
if( sizeof($term_ids) > 0 ) {
$product->set_category_ids( $term_ids );
$product->save();
}
Here below is an example of a hooked function that will auto set the product category terms on product edit.
Note: the product category terms need to exist in woocommerce
// Backend product creation
add_action( 'woocommerce_admin_process_product_object', 'add_product_category_terms_to_product', 100, 1 );
function add_product_category_terms_to_product( $product ){
global $pagenow;
// Only on product Edit
if( $pagenow != 'post.php' ) return; // Exit
if( $term_names = $product->get_attribute( 'Platform' ) )
$term_names = explode( ',', $term_names);
else
return; // Exit
$term_ids = [];
// Loop through the terms
foreach( $term_names as $term_name ) {
// Get the term ID and check if it exist
if( $term_id = term_exists( $term_name, 'product_cat' ) ) {
// Add each term ID in an array
$term_ids[] = $term_id;
}
}
// replace the product categories terms in the product
if( sizeof($term_ids) > 0 ) {
$product->set_category_ids( $term_ids );
}
// save is not needed in the function as this hook does that
}
Code goes in function.php file of your active child theme (or active theme). It should works.
There is a need to display Attribute Variations with their respect "Regular Price". However, despite trying, there is no success to display price. Please see the code below that displays the Variation Fine. Please help out to display the Price as well.
//Getting product attributes
$product_attributes = $product->get_attributes();
if(!empty($product_attributes)){
//Getting product attributes slugs
$product_attribute_slugs = array_keys($product_attributes);
$count_slug = 0;
echo '<div class="product_attributes">';
foreach ($product_attribute_slugs as $product_attribute_slug){
$count_slug++;
// Removing "pa_" from attribute slug and adding a cap to first letter
$attribute_name = ucfirst( str_replace('pa_', '', $product_attribute_slug) );
//echo $attribute_name . ' (';
$attribute_values = get_terms($product_attribute_slug);
$count_value = 0;
//print_r(array_values($available_variations));
foreach($attribute_values as $attribute_value){
$count_value++;
$attribute_name_value = $attribute_value->name; // name value
$attribute_slug_value = $attribute_value->slug; // slug value
$attribute_slug_value = $attribute_value->term_id; // ID value
echo $attribute_name_value;
}
}
echo '</div>';
//print_r(array_values($attribute_values));
}
You can get all the data that you want for all product variations in a variable product this way:
if($product->is_type('variable')){
foreach($product->get_available_variations() as $variation ){
// Variation ID
$variation_id = $variation['variation_id'];
echo '<div class="product-variation variation-id-'.$variation_id.'">
<strong>Variation id</strong>: '.$variation_id.'<br>';
// Attributes
$attributes = array();
foreach( $variation['attributes'] as $key => $value ){
$taxonomy = str_replace('attribute_', '', $key );
$taxonomy_label = get_taxonomy( $taxonomy )->labels->singular_name;
$term_name = get_term_by( 'slug', $value, $taxonomy )->name;
$attributes[] = $taxonomy_label.': '.$term_name;
}
echo '<span class="variation-attributes">
<strong>Attributes</strong>: '.implode( ' | ', $attributes ).'</span><br>';
// Prices
$active_price = floatval($variation['display_price']); // Active price
$regular_price = floatval($variation['display_regular_price']); // Regular Price
if( $active_price != $regular_price ){
$sale_price = $active_price; // Sale Price
}
echo '<span class="variation-prices">
<strong>Price</strong>: '.$variation['price_html'].'</span><br>
</div>';
}
}
This code is tested and works.