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 );
}
}
Related
I am creating a 'Build Your Own' page. After selecting 4 products from the options, 1 main product gets added to the basket with 4 of the selected products being added as meta_data under that main product.
You can see below the main product with 4 selected products (IDs).
After paying for this item, I need to add each selected product to the order, so that they are within the order on the backend. I'm having to do it like this, because I need the stock of the selected product to go down, eventually pulling into a stock management system we use (veeqo)
Any help is appreciated. The code below allows me get the some meta_data for woocommerce_thankyou but I am not sure if it will work then...
add_action('woocommerce_thankyou', 'BuildYourOwn', 10, 1);
function BuildYourOwn( $order_id ) {
if ( !$order_id ){
return;
}
$firstTime = get_post_meta( $order_id, '_thankyou_action_done', true );
// Allow code execution only once
if( !$firstTime ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
$exItems = '';
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
//print_r($item);
// Get the product object
$product = $item->get_product();
// Get the product sku
$product_sku = $product->get_sku();
// Get the product name
$product_id = $product->get_name();
$extras = $item->get_formatted_meta_data('_', true);
$exItems.=$product_sku;
if(!empty($extras)){
$exItems.=$product_sku.' -';
foreach($extras as $extra){
$exItems.= ' ['.$extra->key.' : '. preg_replace("/[^A-Za-z0-9?#,.&%!\s]/","",$extra->value).'] ';
}
}
$exItems.="\n";
}
var_dump($exItems);
Maybe I worded the question wrong - But I figured it out:
I used the code below to get the meta_data which I then looped through and got individual item id and added it to the basket this way.
// Get the product meta data
$extras = $item->get_formatted_meta_data('_', true);
if ($product_id == 60023){
if(!empty($extras)){
$args = array(
'subtotal' => 0,
'total' => 0,
);
foreach($extras as $extra){
$mini_name = $extra->value;
$mini = get_page_by_title( $mini_name, OBJECT, 'product' );
$mini_id = $mini->ID;
$order->add_product( wc_get_product($mini_id), 1, $args); // Add Minis
}
}
}
The only slight issue is woocommerce_thankyou hook not firing if paypal users don't come back to the site after paying.
This question already has answers here:
Get Order items and WC_Order_Item_Product in WooCommerce 3
(2 answers)
Access and display order item meta data in Woocommerce
(2 answers)
Woocommerce - Getting the order item price and quantity.
(3 answers)
Closed 3 years ago.
I am trying to add the product name to the sku when the sku is empty using the following method. So far this works but the last thing is, how do I call the product name within this code?
add_action( 'woocommerce_add_order_item_meta', 'so_28193771', 10, 3 );
function so_28193771( $item_id, $values, $cart_item_key ) {
$item_sku .= get_post_meta( $values[ 'product_id' ], '_sku', true );
if ( empty( $item_sku ) ) {
wc_add_order_item_meta( $item_id, 'sku', $item_sku , false );
}
}
Use the below code inside your function.
WooCommerce 3.0+
Get Product id from order id in Woocommerce
$order = wc_get_order( $order_id );
$items = $order->get_items();
Then if you loop through the items, you can get all the relevant data:
foreach ( $items as $item ) {
$product_name = $item->get_name();
$product_id = $item->get_product_id();
$product_variation_id = $item->get_variation_id();
}
Pre-WooCommerce 3.0
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
}
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).
This question already has an answer here:
Get in WooCommerce cart the product ID of a cart item
(1 answer)
Closed 4 years ago.
I try all of code from google, but none of them works.
such as:
$items = WC()->cart->get_cart();
$product_id = end($items)['data']->post->ID;
For example,
shoes have two different colors: black and gray. I added black shoes, then gray shoes, I order black shoes in the end.
It should return black shoes's ID, but the code on the top will shows gray shoes's id.
I want last added product, not the last position product.
You can put the products into an array and pick the last one using the php end() function as follows:
global $woocommerce;
//get cart items
$items = $woocommerce->cart->get_cart();
$ids = array();
foreach($items as $item => $values) {
$_product = $values['data']->post;
//push each id into array
$ids[] = $_product->ID;
}
//get last product id
$last_product_id = end($ids);
//get product variation details
$variations = get_variation_data_from_variation_id( $last_product_id );
Then define get_variation_data_from_variation_id($item_id) as follows:
//function to get product variation
function get_variation_data_from_variation_id( $item_id ) {
$_product = new WC_Product_Variation( $item_id );
$variation_data = $_product->get_variation_attributes();
//return variation detail
return woocommerce_get_formatted_variation( $variation_data, true );
}
That means you probably access the cart Object before It's created, You will need to place that code inside 'wp' hook for example.
add_action('wp', function(){
$product_id = end( WC()->cart->cart_contents)['product_id'];
});
Edited
I think you were looking for product attribute IDs? Like green or blue etc.
$cart = WC()->cart->get_cart();
$variation = end($cart)['variation'];
// This will have ID, slug, count, etc.
echo json_encode( get_term_by( 'name', end($variation) , str_replace( "attribute_", "", key($variation))) );
If you are specific about you attribute like just size change it like this based on your ta
$cart = WC()->cart->get_cart();
$variation = end($cart)['variation']['attribute_pa_size'];
// This will have ID, slug, count, etc.
echo json_encode( get_term_by( 'name', $variation , 'pa_size') );
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 );
}
}