Get custom values of variations from order items in WooCommerce - php

I am trying to get the variation values selected by customer at checkout from an order (by id or otherwise) to map to variables $storage and $tier which are used to build an API url.
I have tried a variety of methods to get the data onto the variables but the API url is failing, which leads me to believe I do not have the indented values on my variables.
My current code is as follows (extract):
add_action( 'woocommerce_order_status_processing', 'my_function' );
function my_function( $order_id ) {
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $key => $item ) {
$storage = get_post_meta( $key, 'attribute_addon-storage' );
$tier = get_post_meta( $key, 'attribute_subscription-type' );
Does anyone have any idea how I would grab the values of those two variables, addon-storage and subscription-type?
I can see the values I want to get in my database in the woocommerce_order_itemmeta table.
Can I get the values from there?
Edit:
So as per LoicTheAztec's advice, the right way to get the value of the data I see in the woocommerce_order_itemmeta table as $meta_key is:
add_action( 'woocommerce_order_status_processing', 'my_function', 10, 1 );
function my_function( $order_id ) {
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $key => $item ) {
$storage = wc_get_order_item_meta( $key, 'addon-storage', true );
$tier = wc_get_order_item_meta( $key, 'subscription-type', true );
$anyMetaValue = wc_get_order_item_meta( $key, '$meta_key', true );
// Then I can create the variable I need in the the API url with:
$package = "$tier$storage";

Updated: Your are confusing order post meta data and order item meta data, which are very different things and located in different database tables.
Also your code is incomplete and you will not get anything using get_post_meta() with the $key (which is the item ID)...
The get_post_meta( $order_id, 'meta_key', true ) function will look for Order post meta data (not related to order items) and use Order ID as argument.
The wc_get_order_item_meta( $item_id, 'meta_key', true ) function will look for Order item meta data realated and will use the Item ID as argument (the $key in your code)…
So this function should need to be used in the foreach loop, where you get the order items data, instead.
Then 2 ways:
You should check in your database for the last order ID you have (via phpMyAdmin) in wp_postmeta and wp_woocommerce_order_itemmeta tables, to see where is located the data…
Or you can use the following code (just for testing) that will output the order items raw data where your data is located.
This raw data will be output in the shop, archive and product pages, only visible for logged in admins. You will need to define an order ID in it.
Here is this testing function
add_action( 'woocommerce_before_main_content', 'my_testing_order_function' );
function my_testing_order_function() {
// Define an Order ID
$order_id = 724;
// Only for admin user role
if( ! current_user_can('edit_products')) return;
$order = wc_get_order( $order_id ); // The order object
foreach ( $order->get_items() as $item_id => $item ) {
// Order item meta data Raw output
echo "<pre>ORDER ITEM META DATA - (Item_id $item_id):"; print_r($item->get_data()); echo'</pre>';
echo "<pre>ORDER ITEM META META DATA - (Item_id $item_id):"; print_r($item->get_meta_data()); echo'</pre>';
}
}
Now you have everything needed to locate and get the data. The code below should normally work for you, allowing you to get the data from your variations:
add_action( 'woocommerce_order_status_processing', 'my_function', 10, 1 );
function my_function( $order_id ) {
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $key => $item ) {
// get the data
$storage = wc_get_order_item_meta( $item_id, 'attribute_addon-storage', true );
$tier = wc_get_order_item_meta( $item_id, 'attribute_subscription-type', true );
}
}

Related

Add unique line item meta as order meta data in WooCommerce

I have meta data that is stored below the order line items, many times the data is that same. I am trying to save the unique meta values as order meta data, separated by commas and I if possible to add a text "these are your numbers" before this data.
So far I tried the code below but nothing is happening.
add_action( 'woocommerce_update_order', 'add_unique_order_nummers_to_order', 10, 2 );
function add_unique_order_nummers_to_order( $order_id, $order ) {
$allenummers = array();
$items = $order->get_items();
foreach ( $order->get_items() as $item_id => $item ) {
$allenummers[] = $item->get_meta( '_org_ordernummer', true );
}
$ordernummers_array = array_unique($allenummers);
$ordernummers_unique = implode(',', $ordernummers_array);
update_post_meta( $order_id, '_Unieke_nummers', $ordernummers_unique );
}
I hope someone can help.
Update: Use the following simplified code with a different hook, based on your comment, when order status is changed to "we-fact" custom order status:
add_action('woocommerce_order_status_we-fact', 'add_unique_order_nummers_to_order', 20, 2 );
function add_unique_order_nummers_to_order( $order_id, $order ) {
$allenummers = array(); // Initializing
// Loop through order
foreach ( $order->get_items() as $item ) {
$allenummers[] = $item->get_meta( '_org_ordernummer' );
}
if ( ! empty($allenummers) ) {
$order->update_meta_data('_Unieke_nummers', sprintf( __("These are your numbers: %s"), implode(', ', array_unique($allenummers) ) ) );
}
}
Code goes in functions.php file of the active child theme (or active theme). It should works.

WC_Order->get_items() returns empty items

Using the hook 'woocommerce_order_status_completed' I can get $order_id then get the WC_Order object with $order = wc_get_order($order_id). But the following $logger->add("send-order-debug", json_encode($order->get_items()) returns empty item objects
{"257":{},"258":{},"259":{}}
I have no idea why this happening because I can see from the woocommerce orders page that there are actual items inside this order. Does anyone have any idea what is going on?
My end goal is to filter out products that are of category "Subscription" but this is impossible if I can't do $item->get_product_id
function send_order($order_id) {
$order = wc_get_order($order_id);
$logger = wc_get_logger();
$logger->add("send-order-debug", json_encode($order->get_items()));
}
Contents of order object:
Update 1:
You can't use json_encode() on $order->get_items() as you will always get something like "257":{} (where 257 is the item ID) for each order item. So json_encode() fails encoding each order item data located in the array of items, as order items are protected.
Now the only way to JSON encode order items is to unprotect each order item using the WC_Data method get_data() and set it back in the order items array.
This can be done in a compact way using array_map() with a custom function like:
add_action( 'woocommerce_order_status_completed', 'send_order', 10, 2 );
function send_order( $order_id, $order ) {
// Unprotect each order item in the array of order items
$order_items_data = array_map( function($item){ return $item->get_data(); }, $order->get_items() );
$logger = wc_get_logger();
$logger->add("send-order-debug", json_encode($order_items_data));
}
Now it works.
Original answer:
The WC_Order Object is already an included argument in woocommerce_order_status_completed hook, so in your code it should be:
add_action( 'woocommerce_order_status_completed', 'send_order', 10, 2 );
function send_order( $order_id, $order ) {
$order_items = $order->get_items();
}
That works… see this related answers threads…
So the problem is maybe related in the way you try to send the order items using:
$logger->add($TAG, json_encode($order->get_items()));
But it's not possible to help as your code is not testable: the $logger and $TAG variables are not defined in your code.
Now to target subscription products you will use something like:
// Loop through order items
foreach( $order->get_items() as $item ) {
$product = $item->get_product(); // get the WC_Product Object
// Targeting subscription products only
if ( in_array( $product->get_type(), ['subscription', 'subscription_variation'] ) ) {
// Do something
}
}

How to get product categories for the current order item?

I am trying to get the product categories for the woocommerce order item at woocommerce_checkout_create_order_line_item hook.
I am able to successfully get the product_id (thanks to help I got here) but now trying to get the product's categories the array comes back empty trying $product->get_categories() and alternatively trying wc_get_product_category_list($product->get_id().
I can't figure out what I am doing wrong.
add_action('woocommerce_checkout_create_order_line_item',
'add_order_item_custom_meta', 10, 4 );
function add_order_item_custom_meta( $item, $cart_item_key, $cart_item, $order ) {
$product = $item->get_product(); // The WC_Product instance Object
$cat_names = $product->get_categories(); // one attempt
$cat_names = wc_get_product_category_list($product->get_id()); // another attempt
$arrlength = count($cat_names);
for($x = 0; $x<$arrlength; $x++) {
$cat_name = $cat_names[$x];
}
$item->update_meta_data( '_raw_product_id', $product->get_id() );
$item->update_meta_data( '_raw_product_name', $cat_name );
}
So the add_action and the function work. The "$product=..." works and I can use it below in the 2nd to the last line of code as $product->get_id() and the correct value is stored as metadata as desired.
So since $product->get_id() works I thought it logical that $product->get_categories() would work. But it returns a null array.
I then read somewhere that it was deprecated and I should use wc_get_product_category_list. So I tried that and also no luck.
So I am now stuck and can't figure out what is wrong with my code. Thanks for any help.
As you mentioned get_categories() method of WC_Product class is deprecated. Instead you can use get_category_ids() method. However this method returns product category IDs, and It seems that you need category names so we can get the names from WP_Term objects.
Final code would be something like this:
add_action('woocommerce_checkout_create_order_line_item', 'add_order_item_custom_meta', 10, 4 );
function add_order_item_custom_meta($item, $cart_item_key, $cart_item, $order)
{
$product = $item->get_product(); // The WC_Product instance Object
$cat_ids = $product->get_category_ids(); // returns an array of cat IDs
$cat_names = [];
foreach ( (array) $cat_ids as $cat_id) {
$cat_term = get_term_by('id', (int)$cat_id, 'product_cat');
if($cat_term){
$cat_names[] = $cat_term->name; // You may want to get slugs by $cat_term->slug
}
}
$item->update_meta_data( '_raw_product_id', $product->get_id() );
$item->update_meta_data( '_raw_product_name', $cat_names );
}
Note: foreach loop is the preferred way of doing this kind of stuff (instead of using a for loop).

Custom meta data added to Woocommerce not displayed in order item meta

I've a single piece of custom metadata to a WooCommerce order and now I want to display this on the thank you page after checkout, however, the data isn't available. The data is saved and available in the admin, I just can't seem to access it.
function custom_order_item_meta( $item_id, $values ) {
if ( ! empty( $values['custom_option'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'custom_option', $values['custom_option'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'custom_order_item_meta', 10, 2 );
But when I dump out the wc_get_order my meta data isn't there.
I'm using;
woocommerce_add_order_item_meta()
to save the data but dumping out var_dump(wc_get_order( $order->id )); also doesn't show my custom meta field
is there another hook I should be using to access this data?
The data that you are looking for is not order meta data, but order item meta data and is located in wp_woocommerce_order_itemmeta database table (see below how to access this data).
Since woocommerce 3, a much better hook replace old woocommerce_add_order_item_meta hook.
Displayed and readable order item meta data:
To make custom order item meta data displayed everywhere, the meta key should be a readable label name and without starting by an underscore, as this data will be displayed under each order item.
The code:
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_order_item_meta', 20, 4 );
function custom_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( isset( $values['custom_option'] ) ) {
$item->update_meta_data( __('Custom option', 'woocommerce'), $values['custom_option'] );
}
}
In "Order received" (thankyou) page, you will get something like:
This will be displayed too in backend and email notifications.
To access this order item data you need to get items from the order object in a foreach loop:
foreach( $order->get_items() as $item_id => $item ){
$custom_data = $item->get_meta( 'Custom option' );
}
To Get the first order item (avoiding a foreach loop), you will use:
$items = $order->get_items(); // Order items
$item = reset($items); // The first Order item
$custom_data = $item->get_meta( 'Custom option' ); // Your custom meta data
Related: Replace woocommerce_add_order_item_meta hook in Woocommerce 3.4

Add woocommerce variation meta data to order

I'm creating a Woocommerce webshop for a client who is selling different types of breakfasts. Each breakfast has several variations based on the amount of persons.
For example:
Breakfast x (product)
- 1 person (variation)
- 2 persons (variation)
- 3 persons (variation)
- 4 persons (variation)
Each variation contains (different) meta data.
Basically the meta data contains the articles needed to create a breakfast and needs to be added to the order line items in woocommerce when an order is created. The composition of the meta data is different for each variation.
Now I'm able to add meta data to the order item, but somehow meta data gets mixed up when adding two or more products.
Below is my code and output in Woocommerce.
When I use the function just to print the variation meta data instead of adding it to the woocommerce order, I can see that the meta data is correct and the most inner for loop is executed one variation after another. I don't know if wc_add_order_item_meta is causing an issue or maybe the hook I'm calling.
function oc_line_item_metadata( $item_id, $item, $order_id ) {
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach ($items as $item) {
$variation_id = $item['variation_id'];
$product_variation = new WC_Product_Variation( $variation_id );
$meta_data = $product_variation->get_meta_data();
for ($i=0; $i < count($meta_data); $i++) {
$article = ($meta_data[$i]->get_data());
$article_id = $article['id'];
$article_key = $article['key'];
$article_value = $article['value'];
wc_add_order_item_meta( $item_id, $article_key, $article_value );
}
}
}
add_action( 'woocommerce_new_order_item', 'oc_line_item_metadata', 10, 3 );
You can find the output via this link:
https://image.ibb.co/mtsnrb/order.png
Yesterday I found some more information that helped me solve the issue. It seems I'm not using the correct method to get the order_id and variation_id.
Below the correct code that solved my issue:
function oc_line_item_metadata( $item_id, $item, $order_id ) {
$order = wc_get_order( $order_id );
foreach ($order->get_items() as $item_id => $item_obj) {
$variation_id = $item_obj->get_variation_id();
}
$product_variation = new WC_Product_Variation( $variation_id );
$meta_data = $product_variation->get_meta_data();
for ($i=0; $i < count($meta_data); $i++) {
$article = ($meta_data[$i]->get_data());
$article_id = $article['id'];
$article_key = $article['key'];
$article_value = $article['value'];
wc_add_order_item_meta( $item_id, $article_key, $article_value );
}
}

Categories