Need to add custom meta to order items. Googled it and most articles says to use "woocommerce_add_order_item_meta" hook. This hook is deprecated in the newest version 2.3.7. Someone, please tell me which hook to use instead.
http://docs.woothemes.com/wc-apidocs/function-woocommerce_add_order_item_meta.html
2017/2018 THE RIGHT WAY (Using new CRUD setters and Getters methods)
Related: Replace woocommerce_add_order_item_meta hook in Woocommerce 3.4
Since woocommerce 3 that has improved many things making drastic changes, the action hook woocommerce_add_order_item_meta still work perfectly even in woocommerce version 3.3+.
This hook is enabled by WC_Checkout class methods and related functions in the checkout process and not in WC_Order Class where cart data is not anymore available.
Now as Woocommmerce 3 has introduced new CRUD setters and getters methods, the similar replacement hook to be used is woocommerce_checkout_create_order_line_item that has similar useful arguments as cart data.
The woocommerce_new_order_item is really NOT convenient as cart data is not accessible.
Let see how to work with woocommerce_checkout_create_order_line_item. It has 4 available arguments:
$item is an instance of WC_Order_Item_Product new introduced Class
$cart_item_key is the cart item unique hash key
$values is the cart item
$order an instance of the WC_Order object (This is a very useful additional argument in some specific cases)
In this hook we will replace the old working functions wc_add_order_item_meta() by the new WC_Data update_meta_data() method to be used with $item argument.
Example:
## --- New way --- ##
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_checkout_create_order_line_item', 20, 4 );
function custom_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
// Get a product custom field value
$custom_field_value = get_post_meta( $item->get_product_id(), '_meta_key', true );
// Update order item meta
if ( ! empty( $custom_field_value ) ){
$item->update_meta_data( 'meta_key1', $custom_field_value );
}
// … … Or … …
// Get cart item custom data and update order item meta
if( isset( $values['custom_data'] ) ) {
$item->update_meta_data( 'meta_key2', $values['custom_data'] );
}
}
Finally we can do the same with old way using woocommerce_add_order_item_meta hook as it has nearly the same useful arguments:
## --- Old way --- ##
add_action( 'woocommerce_add_order_item_meta', 'custom_add_order_item_meta', 20, 3 );
function custom_add_order_item_meta( $item_id, $values, $cart_item_key ) {
// Get a product custom field value
$custom_field_value = get_post_meta( $values['data']->get_id(), '_meta_key', true );
// Update order item meta
if ( ! empty( $custom_field_value ) ){
wc_add_order_item_meta( $item_id, 'meta_key1', $custom_field_value );
}
// … … Or … …
// Get cart item custom data and update order item meta
if( isset( $values['custom_data'] ) ) {
wc_add_order_item_meta( $item_id, 'meta_key2', $values['custom_data'] );
}
}
Conclusion: woocommerce_checkout_create_order_line_item is the right replacement hook to be used with WooCommerce 3+ and that new CRUD setters and getters methods.
If you look at wc-deprecated-functions.php you will see
/**
* #deprecated
*/
function woocommerce_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique = false ) {
return wc_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique );
}
Basically, the function was renamed to wc_add_order_item_meta(), so if you need the function then use that. The action hook was not renamed and remains in class-wc-checkout.php as:
// Allow plugins to add order item meta
do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
It seems that the hook is now also deprecated as of version 3.0.4.
I'm getting this notification:
The The "woocommerce_add_order_item_meta" hook uses out of date data structures and function is deprecated since version 3.0.4. Replace with woocommerce_new_order_item.
I have replaced the action name 'woocommerce_add_order_item_meta' with 'woocommerce_new_order_item' in an add_action statement in an offending plugin, and the deprecation notification disappears, The problem is that some parameters now appear inside a legacy_values array. I use the plugin YITH WooCommerce Product Add Ons, and the product meta data that should be attached to an order is not picked up by the plugin and therefore not stored with the order. So until this is fixed in the plugin you have to live with the deprecation notification.
I know this has been answered and there is an accepted reply already. I just wanted to give another way to handle this without actually getting a deprecated message (see reference);
add_action('woocommerce_new_order_item', 'saveMetaData', 10, 3); // or use just 2 instead of 3; if you don't need order id
/**
* Add meta to order item
*
* #param int $itemId
* #param WC_Order_Item_Product|WC_Order_Item_Shipping $item
* #param int #orderId
*/
function saveMetaData($itemId, $item, $orderId)
{
if (!isItemValid($item))
{
return;
}
wc_add_order_item_meta($itemId, 'my_custom_data', $item->legacy_values['my_custom_data']);
}
/**
* #param WC_Order_Item_Product|WC_Order_Item_Shipping $item
*
* #return bool
*/
function isItemValid($item)
{
return (
$item instanceof WC_Order_Item_Product &&
isset($item->legacy_values) &&
isset($item->legacy_values['my_custom_data']) &&
!empty($item->legacy_values['my_custom_data'])
);
}
Your specific use case isn't very clear (you didn't specify when or where you need to add this meta info), but you can use woocommerce_checkout_update_order_meta during checkout.
Read more in customizing checkout fields.
No it seems like the hook is also deprecated:
PHP Error:
The "woocommerce_add_order_item_meta" hook uses out of date data structures and function is deprecated since version 3.1.2. Replace with woocommerce_new_order_item.
I also cannot find it here:
https://docs.woocommerce.com/wc-apidocs/hook-docs.html
I wanted to add on to Ilgıt Yıldırım's answer: in my case, my custom values did not exist in the item->legacy_values array. To fix this, I used the woocommerce_checkout_create_order_line_item hook to add the custom values to the item prior to calling the woocommerce_new_order_item hook. Here is an example of that:
add_action( 'woocommerce_checkout_create_order_line_item', 'save_values_in_item', PHP_INT_MAX, 4 );
function save_values_in_item( $item, $cart_item_key, $values, $order ) {
$item->myCustomValues = $values;
}
//THEN call the new hook:
add_action( 'woocommerce_new_order_item', 'add_product_input_fields_to_order_item_meta_wc3', PHP_INT_MAX, 3 );
function add_product_input_fields_to_order_item_meta_wc3( $item_id, $item, $order_id ) {
if ( isset( $item->myCustomValues ) )
{
//iterate through array and place desired values into the meta data using the wc_add_order_item_meta function
}
}
Just to make things clear, this function was deprecated, but the hook is still ok
Related
Im trying to clean up some default database entries that WooCommerce adds to the postmeta table. The two primary entries that I do not need are _customers_ip_address & _customer_user_agent.
I found _customers_ip_address in create_order() function in file class-wc-checkout.php
do_action( 'woocommerce_checkout_create_order', $order, $data ); seems to be what is setting the data. Although I also found it was being set in wc-core-functions.php # function wc_create_order()
Im not 100% sure how to edit this. Im thinking a simple do_filter, but unset seems to not work inside the do_filter, but obviously I am doing it all wrong. Im not that familiar with do_filter but seems like something simple like the code below.
function cleanup_woocommerce_checkout_create_order($order, $data) {
unset($order->set_customer_ip_address());
return $order;
}
add_filter('woocommerce_checkout_create_order', 'cleanup_woocommerce_checkout_create_order');
The code above gives a WordPress Error of :
Fatal error: Can't use method return value in write context
First woocommerce_checkout_create_order is an action hook (but not a filter hook). Also you can not unset any method applied to an object as you are doing.
What you can do is to try setting an empty value, like:
add_action('woocommerce_checkout_create_order', 'cleanup_specific_order_metadata', 10, 2 );
function cleanup_specific_order_metadata( $order, $data ) {
$order->set_customer_ip_address('');
$order->set_customer_user_agent('');
}
It should work.
If it doesn't work, you can try to use woocommerce_checkout_update_order_meta action hook to remove this meta data afterwards once order data has been saved to database, this way:
add_action('woocommerce_checkout_update_order_meta', 'cleanup_specific_order_metadata', 10, 2 );
function cleanup_specific_order_metadata( $order_id, $data ) {
delete_post_meta( $order_id, '_customer_ip_address' );
delete_post_meta( $order_id, '_customer_user_agent' );
}
This last one should work anyways.
As I can see in this article, https://docs.woocommerce.com/wc-apidocs/source-class-WC_Checkout.html#397
do_action( 'woocommerce_checkout_create_order', $order, $data );
so you should use add_action function
add_action('woocommerce_checkout_create_order', 'cleanup_woocommerce_checkout_create_order', 10, 2 );
function cleanup_woocommerce_checkout_create_order( $order, $data ) {
$order->set_customer_ip_address(0);
}
Or updating the post meta
update_post_meta($order_id, '_customer_ip_address', 0);
or
delete_post_meta($order_id, '_customer_ip_address');
I want to remove the order details table in WooCommerce in my functions.php if a if statement is true. I've searched a lot but don't know how to do this.
This is how the file is included in WooCommerce wc-template-functions.php:
if ( ! function_exists( 'woocommerce_order_details_table' ) ) {
/**
* Displays order details in a table.
*
* #param mixed $order_id Order ID.
*/
function woocommerce_order_details_table( $order_id ) {
if ( ! $order_id ) {
return;
}
wc_get_template( 'order/order-details.php', array(
'order_id' => $order_id,
) );
}
}
So I need something like this:
if ( value != true ) {
hide_order_details();
}
Updated (Optionally showing customer details)
You can simply use the following hooked function (that has the $order_id as available argument) with your condition in an if statement (where you will define $value)
The following will remove the order details table in My account > View order:
add_action( 'woocommerce_view_order', 'custom_action_view_order', 5, 1 );
function custom_action_view_order( $order_id ){
$value = false;
if( ! $value ){
remove_action( 'woocommerce_view_order', 'woocommerce_order_details_table', 10 );
## ----- Optionally show customer details (if needed) ----- ##
if ( ! $order = wc_get_order( $order_id ) ) {
return;
}
if( is_user_logged_in() ){
wc_get_template( 'order/order-details-customer.php', array( 'order' => $order ) );
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
1) With customer details:
2) Without customer details:
From what I see, there is no hook you can use in the template.
But you can easily override the order/order-details.php template in your theme to add a condition on whether to output the detail table or not.
The concerned template is in woocommerce/templates/order/order-details.php. You can copy it to your-theme/woocommerce/templates/order/order-details.php and make the required change.
This way, you don't edit original Woocommerce files and use the right way to override woocommerce outputs. Check the order-details template yourself, you'll see there is no hook here permitting to prevent outputting the table. But a simple if wrapper with your condition around the <table> code should do the trick.
Edit: it seems that the filter woocommerce_order_item_visible used in order-details-item.php template can help you to prevent displaying some lines in the order details table. But the template is called within the order detail table html, so you cannot fully remove the table using it.
Note: I'm not sure if this template part is used somewhere else. If it's the case, you should add to your display condition to check if the actual page is the one you want to apply changes on (customer dashboard order detail). If the template is used somewhere else, It'll apply your changes in every places this template is used.
I have custom code use woocommerce_add_order_item_meta hook. But woocommerce 3.4.0 show error log: "woocommerce_add_order_item_meta is deprecated since version 3.0.0! Use woocommerce_new_order_item instead."
How to fix it? Thank you very much. My code:
// add data design to order
function tshirt_order_meta_handler( $item_id, $values, $cart_item_key ) {
if( WC()->session->__isset( $cart_item_key.'_designer' ) ) {
wc_add_order_item_meta( $item_id, "custom_designer", WC()->session->get( $cart_item_key.'_designer') );
}
}
add_action( 'woocommerce_add_order_item_meta', 'tshirt_order_meta_handler', 1, 3 );
Updated
Since Woocommerce version 3, woocommerce_checkout_create_order_line_item action hook now replace outdated woocommerce_add_order_item_meta hook in a much better way using the new introduced CRUD getters and setters methods:
// Save custom data to order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'tshirt_order_meta_handler', 20, 4 );
function tshirt_order_meta_handler( $item, $cart_item_key, $values, $order ) {
$custom_designer = WC()->session->get( $cart_item_key.'_designer' );
if( ! empty($custom_designer) ) {
$item->update_meta_data( 'custom_designer', $custom_designer );
}
}
Code goes in function.php file of your active child theme (or active theme). It should works.
See this related thread:
Woocommerce: Which hook to replace deprecated "woocommerce_add_order_item_meta"
I have a custom field in my products, that needs to be updated through a function, whenever the stock in any of the variations change.
Are there hooks for this? If so, which ones and what is their output ($post_id for example)?
I think you are looking for woocommerce_reduce_order_stock action.
More info about this hook.
Here is a whole list of available hooks.
-- EDIT
Function should look like this:
function test( $order ) { // you get an object $order as an argument
$items = $order->get_items();
$items_ids = array();
foreach( $items as $item ) {
$items_ids[] = $item['product_id'];
}
die( print_r($items_ids) ); // it should break script while reduce stock
}
add_action( 'woocommerce_reduce_order_stock', 'test' );
I have achieved the same using woocommerce_product_set_stock and woocommerce_variation_set_stock hook.
These hooks run when the stock is changed (either increased, or decreased). Even after the stock is decreased after product purchase.
add_action( 'woocommerce_product_set_stock', 'stock_changed' );
add_action( 'woocommerce_variation_set_stock', 'stock_changed' );
function stock_changed( $product ) {
// Do something
}
Since WooCommerce 4.9+, woocommerce_product_before_set_stock and woocommerce_variation_before_set_stock are added to signal that the value of stock_quantity for a product/variation is about to change.
add_action( 'woocommerce_product_before_set_stock', 'stock_about_to_change' );
add_action( 'woocommerce_variation_before_set_stock', 'stock_about_to_change' );
function stock_about_to_change( $product ) {
// Do something
}
During the add_to_cart function, there is a filter to add "cart item data". The filter is woocommerce_add_cart_item_data. I expected to store my custom plugin data in this, so that the data is stored relative to the item and multiple products can be added with different data.
This all seemed to work, but I am not able to retrieve the data. I can't figure it out. The data is there, I can see it in a serialized string, but I can't pull it out.
echo '<pre>';
var_dump( WC() );
foreach( WC()->cart->get_cart() as $cart_item ) {
var_dump( $cart_item );
var_dump( WC()->cart->get_item_data( $cart_item ) );
}
echo '</pre>';
The first dump of WC() has a property: session->_data->cart->(serialized data). The _data property is protected, though, but I can see my custom field inside the serialized data.
The $cart_item is an array with product_id and some other data, but it does not include my custom data :(
Finally, using the get_item_data() method I thought I had it all figured out. I passed in the cart item object, and... an empty string. Same if I pass the key, rather than the cart item itself.
How am I supposed to access the cart item data?
Here is the "Add cart item data" function, which works (or at least seems to work):
function save_class_menu_selection( $cart_item_data, $product_id, $variation_id ) {
if ( !product_is_class( $product_id ) ) return $cart_item_data;
// Save the date, or give a fatal warning. Date is required.
if ( !empty($_REQUEST['class-date']) ) {
$cart_item_data['class-date'] = stripslashes($_REQUEST['class-date']);
return $cart_item_data;
}else{
wp_die('<h2>Invalid Class Date Selected</h2><p>You tried to add a class to your cart, but the date selected was invalid. Please try again.</p>');
exit;
}
}
add_filter( 'woocommerce_add_cart_item_data', 'save_class_menu_selection', 10, 3 );
I was in the same situation today and stumbled over this question after some research.
After some reverse engineering I found the problem and want to provide a solution for other which may also stumble over this question.
The problem is that the data gets sanitized when the cart items get restored from the session. So the extra cart item data IS stored into the session but on the next request it does not get restored.
There is a filter "woocommerce_get_cart_item_from_session". As first parameter you get the sanitized cart item (without extra data) and as second all data which got stored into the session (including extra data).
The solution is to hook in there and also restore your custom cart item data.
Example Code:
add_filter( 'woocommerce_add_cart_item_data', function ( $cartItemData, $productId, $variationId ) {
$cartItemData['myCustomData'] = 'someCustomValue';
return $cartItemData;
}, 10, 3 );
add_filter( 'woocommerce_get_cart_item_from_session', function ( $cartItemData, $cartItemSessionData, $cartItemKey ) {
if ( isset( $cartItemSessionData['myCustomData'] ) ) {
$cartItemData['myCustomData'] = $cartItemSessionData['myCustomData'];
}
return $cartItemData;
}, 10, 3 );
To also show the data at the cart/checkout page you can use the following code:
add_filter( 'woocommerce_get_item_data', function ( $data, $cartItem ) {
if ( isset( $cartItem['myCustomData'] ) ) {
$data[] = array(
'name' => 'My custom data',
'value' => $cartItem['myCustomData']
);
}
return $data;
}, 10, 2 );
The final thing now is to save the data when the order is made:
add_action( 'woocommerce_add_order_item_meta', function ( $itemId, $values, $key ) {
if ( isset( $values['myCustomData'] ) ) {
wc_add_order_item_meta( $itemId, 'myCustomData', $values['myCustomData'] );
}
}, 10, 3 );
You dont have to do anything else the show the data inside the backend, all order item meta data gets display automatically.
I could not get the default cart item data to work, unfortunately. I feel it may not be properly implemented, or may even be deprecated, as there is a lack of support and documentation.
Instead, I used a cart session variable to accomplish the same thing. It's simply an array where each key is the cart_item_key. The value of each array is yet another array, containing a key-value pair of custom fields. So it's essentially the same thing as the built-in cart item data, except stored as cart session data instead.
Here is a Gist containing some utility functions to make it easy:
https://gist.github.com/RadGH/e3444fc661554a0f8c6f
Or if you want to build it yourself, the magic is in WC()->session. Here are the two key functions for this to work:
WC()->session->get('_my_cart_item_data');
WC()->session->set('_my_cart_item_data', $cart_item_data_array);
These are the action hooks you will need:
<<<EXAMPLES
action: woocommerce_add_to_cart
desc: When an item is added to the cart.
args: $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data
action: woocommerce_add_order_item_meta
desc: When a cart item is converted to an order item, save metadata to the order item
using the function "wc_add_order_item_meta( $item_id, $key, $value )"
args: item_id, $values, $cart_item_key