With WooCommerce 3+ introducing new API to fetch the order and it's details, a lot of things have changed and many things break as well.
Consider the following code in my plugin:
$order = wc_get_order($order_id);
$id= 27;
var_dump($order->get_item($id));
which gives me bool(false). I have checked the database and the order and the item does exist.
Also
var_dump($order) does return the entire order object with all the items.
So basically, only the function get_item does not seem to work.
The only explanation is that the ID you are using is not an item_id with a type "line_item"…
I have tried and it works normally as expected using WC_Abstract_Order get_item() method when the item_id is of type "line_item".
To get and check the correct "line_item" Item IDs from a defined Order ID, try:
// define an exiting order ID first
$order_id = 422;
$order = wc_get_order($order_id);
foreach($order->get_items() as $item_id => $item_values){
$item_ids_array[] = $item_id;
}
var_dump( $item_ids_array ); // will output all item IDs (of type "line_item") for this order
## ==> Then now you can try (to check get_item() method):
foreach( $item_ids_array as $item_id ){
var_dump( $order->get_item( $item_id ) ); // Will output each WC_Order_Item_Product Object …
}
This should clarify things.
As reference: How to get WooCommerce order details
Related
I created a custom product type for WooCommerce. With this product type it is possible to connect an other product that is exist by ID.
WooCommerce reduce the stock quantity automatically if an order is placed and the payment is successful.
For example I added a product with ID 4082 to the cart with a quantity from 3.
After place this order WooCommerce updated the stock from product 4082 with -3.
Ok back to my custom product type. As I said it is possible to connect another product by ID.
For example I connect product 4082 with product ID 10988.
If a customer add product 4082 to the cart and placed the order I want reduce the stock quantity from product ID 10988 and not from 4082.
<?php
add_action('woocommerce_checkout_order_processed', 'stocktest');
function stocktest($order_id){
$order = wc_get_order( $order_id );
$order_item = $order->get_items();
foreach( $order_item as $product ) {
//for the topic I programmed the IDs hardcoded
if($product->ID == '4082'){
wc_update_product_stock( 10998, $product['qty'], 'decrease', '' );
}
}
}
?>
I tried the code above and the stock from ID 10998 is correctly decreased but also the stock from ID 4082 is decreased.
Do I use the wrong hook? And how can I make the function correctly?
Hope somebody can help me with this.
Thanks a lot
Does wc_update_product_stock( 4082, $product['qty'], 'increase', '' ); not work?
That way, the stock should be adjusted for the 4082 product.
Also, there might be a potential issue with your snippet. The $product variable refers to the product object of 4082 product, not the 10998 product.
Maybe try something like this?
<?php
add_action('woocommerce_checkout_order_processed', 'stocktest');
function stocktest($order_id){
$order = wc_get_order( $order_id );
$order_item = $order->get_items();
foreach( $order_item as $product ) {
//for the topic I programmed the IDs hardcoded
if($product->ID == '4082'){
$connected_qty = get_post_meta( 10988, '_stock', true );
wc_update_product_stock( 10998, $connected_qty, 'decrease', '' );
wc_update_product_stock( 4082, $product['qty'], 'increase', '' );
}
}
}
?>
Instead of using the custom field query, you could use the WC_Product class:
$connected_product = wc_get_product(10998);
$connected_qty = $connected_product->get_stock_quantity();
I did some research on your question. WooCommerce called a lot of functions to update the stock af order / payment. If an order is placed the items become an status reduce_stock yes or no.
If is yes go further and update the stock of this item. After this WooCommerce created the e-mails and ordernotes.
All this functions worked together please be careful with changing the code in the file wc-stock-functions.php
If you want change and try something do this for example on a local testserver.
This are some functions in the file
<?php
wc_maybe_reduce_stock_levels();
wc_maybe_increase_stock_levels();
wc_reduce_stock_levels();
wc_trigger_stock_change_notifications();
?>
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
}
}
In Woocommerce, I just want to get "local pickup" shipping details to display on a custom email. I tried below functions but they don't show anything for "local pickup".
Which function I can use to get "local pickup" info?
I tried without success the following WC_Order methods:
$order->get_shipping_address_1()
$order->get_formatted_shipping_address()
Edit:
Sorry I did not mention that, but I am using Local Pickup Plus plugin
Edit 2:
This is how I got local pickup info for Local Pickups Plus Plugin docs.woocommerce.com/document/local-pickup-plus which puts meta data to main order variable.
$order = wc_get_order( $order_id );
foreach ($order->get_data() as $key => $value):
if ($key==='shipping_lines'):
foreach ($value as $k=>$v):
$a = $v->get_meta_data();
foreach ($a as $x=>$y):
$t = $y->get_data();
$mykey = $t['key'] ;
$pickup["$mykey"] = $t['value'];
endforeach;
endforeach;
endif;
endforeach;
Then you can use the variables below:
$pickup['_pickup_location_name']
$pickup['_pickup_location_address']['address_1']
$pickup['_pickup_location_phone']['address_2']
$pickup['_pickup_location_address']['postcode']
$pickup['_pickup_location_address']['city']
$pickup['_pickup_location_address']['state'] $pickup['_pickup_location_address']['country']
$pickup['_pickup_location_phone']
$pickup['_pickup_date']
$pickup['_pickup_minimum_hours']
For Order items shipping details refer to: "Get orders shipping method details in WooCommerce 3"
To target order shipping lines details from the WC_Order object you can use the following code:
// Loop though order items shipping
foreach( $order->get_shipping_methods() as $item_id => $item ){
$shipping_item_name = $item->get_name();
$shipping_item_type = $item->get_type();
$shipping_method_title = $item->get_method_title();
$shipping_method_id = $item->get_method_id();
$shipping_method_instance_id = $item->get_instance_id();
$shipping_method_total = $item->get_total();
$shipping_method_total_tax = $item->get_total_tax();
$shipping_method_taxes = $item->get_taxes();
// Get custom meta-data
$formatted_meta_data = $item->get_formatted_meta_data( ' ', true );
// Displaying the row custom meta data Objects (just for testing)
echo '<pre>'; print_r($formatted_meta_data); echo '</pre>';
}
Regarding the custom shipping metadata:
You can access it using the WC_Data method get_meta() from the custom meta "key" located in any custom meta data Objects, like:
$value = $item->get_meta('the_custom_key'); // 'the_custom_key' need to be replaced by the meta "key".
Note: In most Woocommerce email templates and email notification related hooks, you can use the WC_Order object as it's globally included. If not you can get it from the Order ID like:
$order = wc_get_order( $order_id );
Orders related threads:
Get orders shipping method details in WooCommerce 3
Get Order items and WC_Order_Item_Product in Woocommerce 3
How to get WooCommerce order details
Addition - For Local Pickup Plus plugin
It seems that you are using Local Pickup Plus plugin which adds specific custom meta data in the shipping lines.
// Loop though order items shipping
foreach( $order->get_shipping_methods() as $item_id => $item ){
$location_id = $item->get_meta('_pickup_location_id');
$location_name = $item->get_meta('_pickup_location_name');
$location_address = $item->get_meta('_pickup_location_address'); // Array
$location_address_1 = $location_address['address_1'];
$location_address_2 = $location_address['address_2'];
$location_postcode = $location_address['postcode'];
$location_city = $location_address['city'];
$location_state = $location_address['state'];
$location_country = $location_address['country'];
$location_phone = $item->get_meta('_pickup_location_phone');
$pickup_date = $item->get_meta('_pickup_date');
$pickup_min_hours = $item->get_meta('_pickup_minimum_hours');
}
In Woocommerce I am trying to display the results of order item object and to access it:
$product_meta = $item->get_meta_data();
print_r ($product_meta);
This is what I am trying to pull:
EDIT: This is the output that I get using $item->get_formatted_meta_data( '', true ):
To get all order item meta data, you will use WC_Order_Item get_formatted_meta_data() method with specific arguments, this way:
// Accessible non protected Order item meta data
$item_meta_data = $item->get_formatted_meta_data( '', true );
// Formatted raw Output
echo '<pre>'; print_r($item_meta_data); echo '</pre>';
To access some order item properties, you can use any WC_Order_Item_Product method like:
$item->get_product(); // Get the WC_Product object
$item->get_product_id(); // Get the Product ID
$item->get_variation_id(); // Get the Variation ID
$item->get_name(); // Get the Product name
$item->get_quantity(); // Get the item quantity
// and so on …
Then if you need to access a specific "custom" order item data value, you will use WC_Data get_meta() method:
$custom_value = $item->get_meta("_custom_key");
See: Get Order items and WC_Order_Item_Product in Woocommerce 3
Update (displaying your required custom order item meta data)
The data you need can be accessed and displayed this way:
if( $lessons = $item->get_meta('lessons') ) {
echo '<p>Lessons: '.$lessons.'</p>';
}
if( $tour_guide = $item->get_meta('tour guide') ) {
echo '<p>Tour Guide: '.$tour_guide.'</p>';
}
I hope that this works now.
All I did was put this wc_display_item_meta( $item );
and that is it , it pulls the info automatically !!!!!!!
admin can change those in the edit order screen to anything and they will appear
(thanks to #LoicTheAztec for pointing me to the right direction
I am trying to PHP/MYSQL query WooCommerce Product ID/Variation ID from Order ID
If the product(s) in the order is/are simple get product ID(s)
If the product(s) in the order is/are variable get Variation ID(s)
If both (simple and variable) get both (product ID(s) and Variation ID(s))
NOTE: The script I am coding is independent of WordPress.
Using various aspects of the WooCommerce API, this can be accomplished using some version of the following.
$order_id = XXX;
$order = wc_get_order( $order_id ); //returns WC_Order if valid order
$items = $order->get_items(); //returns an array of WC_Order_item or a child class (i.e. WC_Order_Item_Product)
foreach( $items as $item ) {
//returns the type (i.e. variable or simple)
$type = $item->get_type();
//the product id, always, no matter what type of product
$product_id = $item->get_product_id();
//a default
$variation_id = false;
//check if this is a variation using is_type
if( $item->is_type('variable') ) {
$variation_id = $item->get_variation_id();
}
//more code
}
Note the documentation,
wc_get_order();
WC_Order();
WC_Order_Item();
WC_Order_Item_Product();
As you mentioned in note "The script I am coding is independent of WordPress".
Then there will be a query that you can use To get list of variation id from product id.
SELECT ID FROM wp_posts WHERE post_parent = "PRODUCT_ID" AND post_type LIKE 'product_variation'