How to use the add_to_cart() method in WooCommerce? - php

I'm currently adding a product to the shopping cart like this:
$woocommerce->cart->add_to_cart( $product_id, $quantity );
I've noticed the add_to_cart() method actually accepts 5 parameters. See the following (taken from the WooCommerce docs):
add_to_cart( string $product_id, string $quantity = 1, integer $variation_id = '', array $variation = '', array $cart_item_data = array() )
Can someone give an example what the last 3 parameters would be used for?
$variation_id
$variation
$cart_item_data [An array containing any other cart item data for this product.]
There is some description of what these are in the method documentation but it isn't hugely explanatory:
int $variation_id
array $variation attribute values
array $cart_item_data extra cart item data we want to pass into the item
Ref: http://docs.woothemes.com/wc-apidocs/class-WC_Cart.html#_add_to_cart

For the last 3 parameters, you can use them like so:
$variation_id : This would be the specific post ID of the variation, if the product is a variation. If the product you're adding is a simple product or not a variable product, you can leave this blank or set it to 0.
$variation : When you select a variation of a variable product, this array contains what the variation attributes are and what values the customer selected.
$cart_item_data : This is an array of data that allows you to store custom data to the product in your cart. For example, if you wanted to store some meta data about the product that would be useful later on, you can add it here. It will then be available when you loop over your cart items.
Here's an example from a snippet of code I wrote for a plugin:
$gift_item = \wc_get_product($gift_item_id);
$gift_variation_id = 0;
$variation_data = [];
// The Gift is a variation, so get the variation options that are associated with this product variation.
if ($gift_item->is_type('variation')) {
$gift_variation_id = $gift_item->get_id();
$variation_data = wc_get_product_variation_attributes($gift_variation_id);
}
//Check to see if the product is already in our cart and has an item meta of being a gift item
$gift_item_hash = $cart->generate_cart_id($gift_item->get_parent_id() ?: $gift_item->get_id(), $gift_variation_id, $variation_data, [
'gift' => true
]);
if ($cart->find_product_in_cart($gift_item_hash) === false) {
// Add the Gift to our cart. Add some meta data to the item so we can change it's price at checkout.
$gift_cart_item_key = $cart->add_to_cart($gift_item_id, $new_gift_quantity, $gift_variation_id, $variation_data, ['gift' => true]);
}

Related

WooCommerce: add product to cart multiple times as different items but grouped by its meta data

I have only one product ('testare-psihologica'). When the buyer buys it, he must associate this product with additional information (specific 'test' title) that will influence the price. I do not want to use variable products because those 'test' titles will be a lot, so variable products are not very convenient for me. In this situation, I chose to add this product multiple times as separate items but with different 'test' titles.
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_simple_product_custom_price', 20, 2 );
function add_cart_simple_product_custom_price( $cart_item_data, $product_id ) {
//add selected test ID to 'testare-psihologica' cart item
if( isset( $_GET['test'] ) ) {
$test_id = get_page_by_path( $_GET['test'], OBJECT, 'tests' )->ID;
$test_title = get_post_field( 'post_title', $test_id );
$cart_item_data['test_title'] = $test_title;
}
// add product to cart multiple times, but as different items
$unique_cart_item_key = md5( microtime() . rand() );
$cart_item_data['unique_key'] = $unique_cart_item_key;
return $cart_item_data;
}
After this, the 'test' titles will be displayed in the cart and checkout pages under the product name, and then they will be added to the order as metadata (with other code).
The only disadvantage of this approach is that when the product is added to the cart several times but with the same (identical) 'test' title, it also appears as separate items, but I would like these items to appear grouped as one and only increase their quantity. So, instead of this:
I want this:
And I want to do this programmatically. How can this be achieved?
It seems that the following lines of code are redundant:
$unique_cart_item_key = md5( microtime() . rand() );
$cart_item_data['unique_key'] = $unique_cart_item_key;
So to answer your question you can just delete those lines
Because I don't have the same get data that you use in your answer, I made a proof of concept, divided into 3 steps:
1. Add input field before the add to cart button on the single product page
function action_woocommerce_before_add_to_cart_button() {
// Add a new input field, allowing the customer to set "test"
echo '<div class="my-div"><input type="text" id="test" name="test"></div>';
}
add_action( 'woocommerce_before_add_to_cart_button', 'action_woocommerce_before_add_to_cart_button' );
2. Add 'test' value to cart item
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
// Add test to cart item
if ( ! empty ( $_POST[ 'test' ] ) ) {
$cart_item_data['test_title'] = sanitize_text_field( $_POST['test'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
3. Optionally display custom data in cart and checkout pages
// Optionally display custom data in cart and checkout pages
function filter_woocommerce_get_item_data( $cart_data, $cart_item = null ) {
if ( isset( $cart_item['test_title'] ) ) {
$cart_data[] = array(
'name' => 'Test title',
'value' => $cart_item['test_title']
);
}
return $cart_data;
}
add_filter( 'woocommerce_get_item_data', 'filter_woocommerce_get_item_data', 99, 2 );
Then apply the following steps on the first step:
Insert group1 as value and press the add to cart button
Insert group2 as value and press the add to cart button
Insert group1 as value and press the add to cart button
The product with value group1 will automatically be grouped based on the corresponding value:

My cart_item_data isn't showing up in the order

I'm working on a site that lets a user enter data from a form and have it attached to a product. I was originally using:
$cart_item_data ['Entry Link'] = $formUrl;
$woocommerce->cart->add_to_cart($productID,$quantity,0,$cart_item_data);
With $formUrl being a link to the form data. Whenever a user made an order, under the product would be 'Entry Link' with the url.
We later had to add variations to the product so the line looked like:
$woocommerce->cart->add_to_cart($productID,$quantity,$typeID,$cart_item_data);
With $typeID being the variation ID. Once I added the $typeID, only the variation showed up on the order in the backend and not the 'Entry Link'.
If I reset $typeID to '0', the entry link shows up when an order is made. I also tried using the variation id in place of the product id but was still getting the same issue. I looked at the documentation and it should be working.
I need both the variation and the 'Entry Link' to be entered and visible from the backend.
This was the part requested by Vincenzo Di Gaetano in the comments
$formUrl = $_SERVER['SERVER_NAME'].'/wp-admin/admin.php?page=gf_entries&view=entry&id='.$formID.'&lid='.$entryID;
$cart_item_data['Entry Link'] = $formUrl;
$variationAttributes['Per'] = 1000;
$woocommerce->cart->add_to_cart($productID,$quantity,$typeID,$variationAttributes,$cart_item_data);
I added variationAttributes to it with 'Per' lining up with the attribute name, '1000' represents the value of that attribute. I hardcoded it in for testing. When I echo just the $formUrl, it does return the correct url
You are setting only 4 arguments instead of 5 for the add_to_cart method of the WC_Cart class.
Try replacing this:
$woocommerce->cart->add_to_cart( $productID, $quantity, $typeID, $cart_item_data );
with:
$woocommerce->cart->add_to_cart( $productID, $quantity, $typeID, $variation, $cart_item_data );
Here is an example of how to add a variation to the cart:
WordPress WooCommerce - Add a variable product to cart using the WC_Cart class
This will not be enough to display the cart item data as the meta of the order item.
Based on this question Save and display product custom meta on WooCommerce orders and emails you will need to save the value as a order item meta data:
// save the cart item data as custom order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'save_cart_item_data_as_order_item_meta_data', 20, 4 );
function save_cart_item_data_as_order_item_meta_data( $item, $cart_item_key, $values, $order ) {
if ( isset( $values['Entry Link'] ) ) {
$item->update_meta_data( __( 'Entry Link'), $values['Entry Link'] );
}
}
The code has been tested and works. Add it to your active theme's functions.php.
Here is the result:

How to update WooCommerce order item quantity

I need to update the order item meta in a woocommerce oder on checkout page or while woocommerce creates the order.
I'm using the plugin visual product configurator and it is not passing the right quantity of some items of the order to woocommerce order meta, especially when I use multiple variations on the same product.
Is there a hook for me to use to update the item quantity for a certain order item and how can I use it?
The plugin returns me an array with all the cart information and I can only check if an item of the order appears multiple times - if yes I need to change the quantity of that item to that number in the woocommerce order/database.
I was thinking of adding the following hook to my functions.php
add_action('woocommerce_checkout_create_order', 'change_qty', 1,1);
function change_qty($item_qty){
foreach($item_qty as $qty) {
$qty['product_id'] = $id;
$qty['qty'] = $new_qty
$order->update_meta_data('quantity', $new_qty, $id)
}
}
Whereas $item_qty is be an multi-dimensional array containing the item_ids and adjusted quantities.
Another problem I'm facing is that I dont know when I need to call that function because I get the array from the plugin on the checkout page, but I think WooCommerce has not yet created an order at that moment?
The result should be an adjusted item quantity in the woocommerce order summary in the backend.
To update the order item quantity, you can use WC_Order_Item_Product set_quantity() method.
The correct hook to update order items (line items) is woocommerce_checkout_create_order_line_item action hook, that is triggered during order creation, before data is saved to databased.
add_action('woocommerce_checkout_create_order_line_item', 'change_order_line_item_quantity', 10, 4 );
function change_order_line_item_quantity( $item, $cart_item_key, $cart_item, $order ) {
// Your code goes below
// Get order item quantity
$quantity = $item->get_quantity();
$new_qty = $quantity + 2;
// Update order item quantity
$item->set_quantity( $new_qty );
}
The function arguments (variables) are defined and usable:
$item is the WC_Order_Item_Product Object (not saved yet to database)
$cart_item_key is the related cart item key
$cart_item is the related cart item data
$order is the WC_Order Object (not saved yet to database)
Related:
Get Order items and WC_Order_Item_Product in Woocommerce 3
WC_Order_Item_Product Class and methods API Documentation
WC_Checkout and woocommerce_checkout_create_order_line_item action hook located in the create_order_line_items() method
This can help you (we hook into payment completed notification from the payment provider). If you want to update the _qty just after the order was created, I can change my function. But for now I would update it only when the payment was successful.:
/**
* Update order item qty after payment successful
*/
add_filter( 'woocommerce_payment_complete_order_status', 'update_order_item_qty', 10, 2 );
function update_order_item_qty( $order_status, $order_id ) {
//Get the order and items
$order = new WC_Order( $order_id );
$items = $order->get_items();
//New qty
$new_qty = 0;
foreach ( $items as $item_id => $item_data ) {
update_meta_data( '_qty', $new_qty, $item_id );
}
}
Please try if this is what you'r looking for.

Prevent customers from ordering from more than 3 Vendors in Woocommerce

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…

Displaying product custom fields values in the order once processed

On woocommerce, I am using the code below to render some product custom fields, on cart and on checkout:
add_filter( 'woocommerce_get_item_data', 'rendering_meta_field_on_cart_and_checkout', 10, 2 );
function rendering_meta_field_on_cart_and_checkout( $cart_data, $cart_item ) {
$custom_items = array();
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item['wccpf_enter_product_id'] ) ) {
$diamond = $cart_item['wccpf_enter_product_id'];
$pacolor = get_the_terms($diamond, 'pa_color');
foreach ( $pacolor as $pacolor ) {
$color= $pacolor ->name;
}
$custom_items[] = array( "name" => "color", "value" => $color);
}
return $custom_items;
}
How can I display that custom product fields wccpf_enter_product_id' value in orders?
Thanks.
You can use a custom function hooked in woocommerce_add_order_item_meta action hook to achieve this.
You will need first to add an attribute in your products to get a "readable clean label" for your custom field value that is going to appear as order items meta data.
For that you have to create an attribute first and then set it in your product with any value (as you will replace this value in the code below).
See HERE some more explanations about that process…
You will have to replace in my code the 'custom_field_key' by your specific custom key that you will find on wp_woocommerce_order_itemmeta MySQL table for the corresponding item ID for your specific Order ID.
To find the corresponding item ID for the order, you can search in wp_woocommerce_order_items MySQL table with the Order ID…
You will have also to set your correct attribute slug instead of 'pa_your_attribute' to display in your orders the correct label text for this custom field value.
(see below other similar answers references).
So your code will be something like this:
// ADD THE INFORMATION AS META DATA SO THAT IT CAN BE SEEN AS PART OF THE ORDER
add_action('woocommerce_add_order_item_meta','add_and_update_values_to_order_item_meta', 1, 3 );
function add_and_update_values_to_order_item_meta( $item_id, $item_values, $item_key ) {
// Getting your custom product ID value from order item meta
$custom_value = wc_get_order_item_meta( $item_id, 'custom_field_key', true );
// Here you update the attribute value set in your simple product
wc_update_order_item_meta( $item_id, 'pa_your_attribute', $custom_value );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This should work
Related answers:
Adding user custom field value to order items details
Add custom Product data dynamically as item meta data on the Order
Displaying custom product data in Order items view

Categories