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
Related
We have created a site using WooCommerce and WordPress with predefined products.
However, Product A must have variations, to resolve this, we added custom fields such as Colors, Dimensions, etc...
Now once the end-users are navigating to the product, we are able to fetch the custom field values. We also modified the UI so that users can pick and choose from these custom fields. (We have a preview of the modifications done via JS/CSS), so for instance, if they choose a green color we use JS to add a layer of green so that the preview is real time.
Now, we have one challenge.
-> What is the best way to go about adding this product PLUS all modifications done to the cart?
So for instance Product A was modified (on the front-end) using data pulled from the custom fields to include Color: Green and Size: 100x100 instead of defaults values. How do we store this and pass the customized product to the cart?
Appreciate the help!. Glad to add more details if something is not clear.
(There are plugins out there that can provide this functionality; Things similar to WooCommerce Product Add-On, etc... However, we have to custom develop the feature.)
It requires some steps.
1). For your product page:
First you might need to add hidden fields to the add to cart form for each custom field like (example):
add_action( 'woocommerce_before_add_to_cart_button', 'add_hidden_empty_input_fields' );
function add_hidden_empty_input_fields() {
?>
<input type="hidden" name="hidden_color" id="hidden_color" value="">
<input type="hidden" name="hidden_dimensions" id="hidden_dimensions" value="">
<?php
}
Then you will have to make some changes to your Javascript code to set the custom fields chosen values in those hidden fields.
2). Pass the custom fields selected values as custom cart item data (example):
add_filter('woocommerce_add_cart_item_data', 'add_custom_field_data', 10, 3 );
function add_custom_field_data( $cart_item_data, $product_id, $variation_id ) {
if ( isset($_POST['hidden_color']) && ! empty($_POST['hidden_color']) ) {
$cart_item_data['color'] = esc_attr($_POST['hidden_color']);
}
if ( isset($_POST['hidden_dimensions']) && ! empty($_POST['hidden_dimensions']) ) {
$cart_item_data['dimensions'] = esc_attr($_POST['hidden_dimensions']);
}
return $cart_item_data;
}
And optionally display values on cart items (example):
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
function display_custom_cart_item_data( $cart_data, $cart_item ) {
if ( isset($cart_item['color']) ) {
$cart_data[] = array( "name" => __("Color"), "value" => $cart_item['color'] );
}
if ( isset($cart_item['dimensions']) ) {
$cart_data[] = array( "name" => __("Dimensions"), "value" => $cart_item['dimensions'] );
}
return $cart_data;
}
3). Save the custom cart item data as custom order item meta data (example):
add_action( 'woocommerce_checkout_create_order_line_item', 'save_custom_order_item_meta_data' , 10, 4 );
function save_custom_order_item_meta_data( $item, $cart_item_key, $values, $order ) {
if ( isset($values['color']) ) {
$item->add_meta_data( __("Color"), $values['color'] );
}
if ( isset($values['dimensions']) ) {
$item->add_meta_data( __("Dimensions"), $values['dimensions'] );
}
}
Note: The data is going to be displayed on everywhere on orders and email notifications.
Code goes in functions.php file of the active child theme (or active theme). It should work.
The variations have a post_id, you would just use the variation post_id instead of the main product one.
I am using a plugin, WooCommerce Custom Post Type Manager, along with WooCommerce that enables the use of custom post types as products. This is working pretty well for the most part, but I want to be able to control inventory and I am able to see the problem. In the database under the order_item_meta table, the product_id = 0. Because the product_id is empty, there is no way to update the stock on a completed purchase.
I know that WooCommerce made some changes to where it searched if a post_type was a 'product' and if not, certain things failed. I am wondering if there is a filter hook or another way to add the product id with a custom function at checkout?
This is the function I am trying to create to control a custom type of inventory, we are selling event "seats". And no, even the regular "stock" does not work, probably because of the same reason.
function update_course_seats( $order_id ){
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item->get_product_id(); // this returns 0
if($product_id != 0) {
wc_update_order_item_meta( $item, '_seats', 10 ); // example number
}
}
//add_action( 'woocommerce_payment_complete', 'update_course_seats');
First you should use woocommerce_add_cart_item_data action hook, to store your Custom Post Type (CTP) Id on add to cart… But for that you will need to display, on your CTP pages, inside the add to cart form, a hidden field with the CTP Id like:
<input type="hidden" name="ctpost_id" value="<?php echo get_the_id(); ?>">
Now you can add this hooked function, that will add as custom cart item data your CTP Id:
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 3 );
function add_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ){
if( isset( $_POST['ctpost_id'] ) ) {
$cart_item_data['ctpost_id'] = wc_clean( $_POST['ctpost_id'] );
}
return $cart_item_data;
}
The Second hook to be used is woocommerce_checkout_create_order_line_item action hook, to add custom order item meta data (or to make changes to order item data). This hook is triggered before payment gateways process during order creation.
You can use WC_data add_meta_data() method to save your CTP Id as a custom CTP ID like:
add_action( 'woocommerce_checkout_create_order_line_item', 'save_cpt_id_to_order_item_data', 10, 4 );
function save_cpt_id_to_order_item_data( $item, $cart_item_key, $cart_item, $order ){
if( isset($cart_item['ctpost_id']) && $cart_item['ctpost_id'] > 0 ) {
// Add the custom CTP post ID
$item->add_meta_data('_ctpost_id', $cart_item['ctpost_id'] );
}
// And here for example you add seats as custom cart item data
if( isset($cart_item['quantity']) ) {
$item->add_meta_data( 'Seats', $cart_item['quantity'], true );
}
}
When the order will get paid, you will be able to update everything required, as you will have your CTP post ID as custom order item data.
Then finally, you can use woocommerce_payment_complete as follow:
add_action( 'woocommerce_payment_complete', 'action_payment_complete_callback', 10, 1 );
function action_payment_complete_callback( $order_id ){
$order = wc_get_order();
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
$ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
// Your code goes here
}
}
Or woocommerce_order_status_changed hooks like:
add_action( 'woocommerce_order_status_changed', 'action_order_status_changed_callback', 10, 4 );
function action_order_status_changed_callback( $order_id, $status_from, $status_to, $order ){
if( in_array( $status_to, ['processing','completed'] ) ) {
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
$ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
// Your code goes here
}
}
}
Related: Get Order items and WC_Order_Item_Product in Woocommerce 3.
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
I have found similar solutions to this, for example: "How to limit orders to one category". I have tried to modify the code but it's not specific enough.
In my case, each Vendor is defined by a product attribute value, 8 Terms at all. If the cart contains products from more than 3 different Terms, I need to set up a message that says "Sorry, you may only order from 3 different Vendors at a time".
This is what I'm using as a starting point:
add_action( 'woocommerce_add_to_cart', 'three_vendors' );
function three_vendors() {
if ATTRIBUTE = SELECT A VENDOR; NUMBER OF TERMS > 3 {
echo "Sorry! You can only order from 3 Vendors at a time.”;
}
}
The middle line is me filling in the blanks using non-php language.
I am looking for a way to define the amount of variations in the cart. If this is not possible to do with Attributes, I am open to use Categories instead.
Does anyone know how to do this?
Update: Is not possible to manage variations with woocommerce_add_to_cart_valisation hook
Instead we can use a custom function hooked in woocommerce_add_to_cart filter hook, removing the last added cart item when more than 3 vendors (items) are in cart:
// Remove the cart item and display a notice when more than 3 values for "pa_vendor" attibute.
add_action( 'woocommerce_add_to_cart', 'no_more_than_three_vendors', 10, 6 );
function no_more_than_three_vendors( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) {
// Check only when there is more than 3 cart items
if ( WC()->cart->get_cart_contents_count() < 4 ) return;
// SET BELOW your attribute slug… always begins by "pa_"
$attribute_slug = 'pa_vendor'; // (like for "Color" attribute the slug is "pa_color")
// The current product object
$product = wc_get_product( $variation_id );
// the current attribute value of the product
$curr_attr_value = $product->get_attribute( $attribute_slug );
// We store that value in an indexed array (as key /value)
$attribute_values[ $curr_attr_value ] = $curr_attr_value;
//Iterating through each cart item
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// The attribute value for the current cart item
$attr_value = $cart_item[ 'data' ]->get_attribute( $attribute_slug );
// We store the values in an array: Each different value will be stored only one time
$attribute_values[ $attr_value ] = $attr_value;
}
// We count the "different" values stored
$count = count($attribute_values);
// if there is more than 3 different values
if( $count > 3 ){
// We remove last cart item
WC()->cart->remove_cart_item( $cart_item_key );
// We display an error message
wc_clear_notices();
wc_add_notice( __( "Sorry, you may only order from 3 different Vendors at a time. This item has been removed", "woocommerce" ), 'error' );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and should works for you. It will check for the attribute values regarding your specific attribute and will remove last added cart item for more than 3 attribute values.
The only annoying thing is that I can't remove the classic added to cart notice for the moment. I will try to find another way…
I have created a plugin mainly following this guide, which simply adds a small bit of data to a given product.
I know Woocommerce have made some changes outlined here.
The problem I'm having is that when I add my item to the cart, and access the cart page, I am getting a blank screen. I believe the problem stems from the use of this filter:
add_filter('woocommerce_get_cart_item_from_session'...
If I comment the line with this filter, my checkout page works (but without the extra details added to my product). I can't work out why this filter is not working, or what problem it is having??
The woocommerce changes said:
WooCommerce 2.0 no longer uses the PHP session_start function, instead it makes use of WordPress’ transients, which is great, unless your code happens to rely on $_SESSION.
I'm not starting any new sessions as far as I can see (my code is 90% the same as the first link). Maybe this is a problem with my server? Any ideas?
i was browsing a lot and i recommend that you read the following:
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.
this is from
How to retrieve cart_item_data with WooCommerce?
you have to add this stuff to the functions.php file of your theme for example.