I'm trying to fire a function when the quantity of a product is changed in cart.
More specifically I want to run this function when a customer modify the amount in a cart.
I'm looking to find the amount left in a cart then to intercept the update cart event
Currently I'm using:
add_action( 'woocommerce_remove_cart_item', 'my function');
When I press "update_cart", it doesn't seem to work.
Any advice?
Thank you!
You should use woocommerce_after_cart_item_quantity_update action hook that has 4 arguments. But when quantity is changed to zero, woocommerce_before_cart_item_quantity_zero action hook need to be used instead (and has 2 arguments).
Below is a working example that will limit the updated quantity to a certain amount and will display a custom notice:
add_action( 'woocommerce_after_cart_item_quantity_update', 'limit_cart_item_quantity', 20, 4 );
function limit_cart_item_quantity( $cart_item_key, $quantity, $old_quantity, $cart ){
if( ! is_cart() ) return; // Only on cart page
// Here the quantity limit
$limit = 5;
if( $quantity > $limit ){
// Change the quantity to the limit allowed
$cart->cart_contents[ $cart_item_key ]['quantity'] = $limit;
// Add a custom notice
wc_add_notice( __('Quantity limit reached for this item'), 'notice' );
}
}
This code goes on function.php file of your active child theme (or theme). Tested and works.
As this hook is located in WC_Cart set_quantity() method, is not possible to use that method inside the hook, as it will throw an error.
To trigger some action when quantity is set to Zero use:
add_action( 'woocommerce_before_cart_item_quantity_zero', 'action_before_cart_item_quantity_zero', 20, 4 );
function action_before_cart_item_quantity_zero( $cart_item_key, $cart ){
// Your code goes here
}
Maybe this hook? do_action( 'woocommerce_after_cart_item_quantity_update', $cart_item_key, $quantity, $old_quantity );
http://hookr.io/actions/woocommerce_after_cart_item_quantity_update/
Related
I am running a WooCommerce store with WC Marketplace. What I am trying to achieve with the hook below is to prevent a new item being added to a basket if there is already a product in the basket from a different vendor. e.g. If a shopper adds product x from vendor y to his basket, if they were to add product a from vendor b, then the item will not be added and the user will be informed of the error.
I have 2 questions:
- firstly when does a hook run, is it before the main function fired or after? I have a hook for the function woocommerce_add_to_cart. So I want to know will the hook fire after the function woocommerce_add_to_cart runs or before.
- My second question is, I have attached the hook below, in your opinion would this work?
function action_woocommerce_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) {
$same_vendor = 1;
$empty = WC_Cart::is_empty();
//If there is an item in the cart then,
if (!$empty) {
//Get the VendorId of the product being added to the cart.
$vendor = get_wcmp_product_vendors($product_id);
$vendor_id = $vendor->id;
foreach( WC()->cart->get_cart() as $cart_item ) {
//Get the vendor Id of the item
$cart_product_id = $cart_item['product_id'];
$cart_vendor = get_wcmp_product_vendors($product_id);
$cart_vendor_id = $cart_vendor->id;
//If two products do not have the same Vendor then set $same_vendor to 0
if($vendor_id !== $cart_vendor_id) {
$same_vendor = 0;
}
}
if ($same_vendor === 0) {
WC()->cart->remove_cart_item( $cart_item_key );
//How do I show a message to tell the customer.
}
}
}
Regards
Here below are the hooks involved in WC_Cart add_to_cart() method:
A) Before an item is added to cart:
Validation filter hook woocommerce_add_to_cart_validation
Item Quantity change filter hook woocommerce_add_to_cart_quantity (not with ajax)
Item Data change filter hook woocommerce_add_cart_item_data (not with ajax)
and some others related to "sold individually" products (see here)
A) After an item is added to cart:
Change cart Item filter hook woocommerce_add_cart_item
Add an event, action hook woocommerce_add_to_cart
To be clear in your case:
As you can see now woocommerce_add_to_cart is not a function but only an action hook.
Hook location: It's located inside WC_Cart add_to_cart() method (at the end of the source code).
When the hook is fired: It's fired once the WC_Cart add_to_cart() method is executed.
What is the purpose: To execute some custom code when this method is executed (event).
Regarding your code:
It should be better to use the dedicated filter hook woocommerce_add_to_cart_validation that will stop customer that want to add a new item to the cart if there is already a product in cart from a different vendor, displaying optionally a custom message:
add_filter( 'woocommerce_add_to_cart_validation', 'filter_add_to_cart_validation', 10, 3 );
function filter_add_to_cart_validation( $passed, $product_id, $quantity ) {
if ( WC()->cart->is_empty() ) return $passed;
// Get the VendorId of the product being added to the cart.
$current_vendor = get_wcmp_product_vendors($product_id);
foreach( WC()->cart->get_cart() as $cart_item ) {
// Get the vendor Id of the item
$cart_vendor = get_wcmp_product_vendors($cart_item['product_id']);
// If two products do not have the same Vendor
if( $current_vendor->id != $cart_vendor->id ) {
// We set 'passed' argument to false
$passed = false ;
// Displaying a custom message
$message = __( "This is your custom message", "woocommerce" );
wc_add_notice( $message, 'error' );
// We stop the loop
break;
}
}
return $passed;
}
Code goes in function.php file of your active child theme (or active theme) or in any plugin file.
Tested and works.
I need to add an event to the quantity selector on the cross-sells on my cart since there bizarrely seems to be no onchange event there.
Normally this would be easy but with WP and WC it's all shortcodes etc so impossible to find the actual place where I can a) correct this and b) not have it overwritten every update, especially since every qty selector should change the data-quantity by default.
cross-sells.php just gives me wc_get_template_part( 'content', 'product' ) which just leads me on to content-product.php which just gives me a load of do_actions.
Specifically, I'd like to correct an error where my content-product.php doesn't seem to have quantity event triggers (ie it doesn't change the data-quantity), but ideally, I'd like to know how I edit any section I want, even if only to improve the awful layout of WC.
Thanks.
You may be looking for some hook like this if am not wrong.
add_action( 'woocommerce_after_cart_item_quantity_update', 'limit_cart_item_quantity', 20, 4 );
function limit_cart_item_quantity( $cart_item_key, $quantity, $old_quantity, $cart ){
if( ! is_cart() ) return; // At Cart page
// Limit quantity
$limit = 5;
if( $quantity > $limit ){
$cart->cart_contents[ $cart_item_key ]['quantity'] = $limit;
wc_add_notice( __('Quantity limit reached for this item'), 'notice' );
}
}
I would like Woocommerce to only allow 1 product in the cart. If a product is already in the cart and another one is added then it should remove the previous one.
I found this code on net:
/**
* When an item is added to the cart, remove other products
*/
function custom_maybe_empty_cart( $valid, $product_id, $quantity ) {
if( ! empty ( WC()->cart->get_cart() ) && $valid ){
WC()->cart->empty_cart();
wc_add_notice( 'Only allowed 1 item in cart, please remove previous
item.', 'error' ); // here instead popup notice need to remove prevous added product
}
return $valid;
}
add_filter( 'woocommerce_add_to_cart_validation', 'custom_maybe_empty_cart',
10, 3 );
But it seems that is not working as I wanted. It need to autoremove previously added product in cart, and add latest added product in cart. Can someone to give me tip what need to update on class to work in latest woo 3.3.X ?
This one is the more compact and actual code as global $woocommerce is not needed anymore:
add_filter( 'woocommerce_add_to_cart_validation', 'auto_empty_cart', 20, 3 );
function auto_empty_cart( $passed, $product_id, $quantity ) {
if( WC()->cart->is_empty() ) return $passed; // Exit
WC()->cart->empty_cart();
return $passed;
}
Code goes in function.php file of your active child theme (or active theme).
Tested and works in all Woocommerce versions since 2.6.x
This working perfecty on Woocommerce 3.3.X
add_filter( 'woocommerce_add_to_cart_validation',
'bbloomer_only_one_in_cart', 99, 2 );
function bbloomer_only_one_in_cart( $passed, $added_product_id ) {
global $woocommerce;
// empty cart: new item will replace previous
$woocommerce->cart->empty_cart();
// display a message if you like
wc_add_notice( 'Product added to cart!', 'notice' );
return $passed;
}
I am running a WooCommerce store with WC Marketplace. What I am trying to achieve with the hook below is to prevent a new item being added to a basket if there is already a product in the basket from a different vendor. e.g. If a shopper adds product x from vendor y to his basket, if they were to add product a from vendor b, then the item will not be added and the user will be informed of the error.
I have 2 questions:
- firstly when does a hook run, is it before the main function fired or after? I have a hook for the function woocommerce_add_to_cart. So I want to know will the hook fire after the function woocommerce_add_to_cart runs or before.
- My second question is, I have attached the hook below, in your opinion would this work?
function action_woocommerce_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) {
$same_vendor = 1;
$empty = WC_Cart::is_empty();
//If there is an item in the cart then,
if (!$empty) {
//Get the VendorId of the product being added to the cart.
$vendor = get_wcmp_product_vendors($product_id);
$vendor_id = $vendor->id;
foreach( WC()->cart->get_cart() as $cart_item ) {
//Get the vendor Id of the item
$cart_product_id = $cart_item['product_id'];
$cart_vendor = get_wcmp_product_vendors($product_id);
$cart_vendor_id = $cart_vendor->id;
//If two products do not have the same Vendor then set $same_vendor to 0
if($vendor_id !== $cart_vendor_id) {
$same_vendor = 0;
}
}
if ($same_vendor === 0) {
WC()->cart->remove_cart_item( $cart_item_key );
//How do I show a message to tell the customer.
}
}
}
Regards
Here below are the hooks involved in WC_Cart add_to_cart() method:
A) Before an item is added to cart:
Validation filter hook woocommerce_add_to_cart_validation
Item Quantity change filter hook woocommerce_add_to_cart_quantity (not with ajax)
Item Data change filter hook woocommerce_add_cart_item_data (not with ajax)
and some others related to "sold individually" products (see here)
A) After an item is added to cart:
Change cart Item filter hook woocommerce_add_cart_item
Add an event, action hook woocommerce_add_to_cart
To be clear in your case:
As you can see now woocommerce_add_to_cart is not a function but only an action hook.
Hook location: It's located inside WC_Cart add_to_cart() method (at the end of the source code).
When the hook is fired: It's fired once the WC_Cart add_to_cart() method is executed.
What is the purpose: To execute some custom code when this method is executed (event).
Regarding your code:
It should be better to use the dedicated filter hook woocommerce_add_to_cart_validation that will stop customer that want to add a new item to the cart if there is already a product in cart from a different vendor, displaying optionally a custom message:
add_filter( 'woocommerce_add_to_cart_validation', 'filter_add_to_cart_validation', 10, 3 );
function filter_add_to_cart_validation( $passed, $product_id, $quantity ) {
if ( WC()->cart->is_empty() ) return $passed;
// Get the VendorId of the product being added to the cart.
$current_vendor = get_wcmp_product_vendors($product_id);
foreach( WC()->cart->get_cart() as $cart_item ) {
// Get the vendor Id of the item
$cart_vendor = get_wcmp_product_vendors($cart_item['product_id']);
// If two products do not have the same Vendor
if( $current_vendor->id != $cart_vendor->id ) {
// We set 'passed' argument to false
$passed = false ;
// Displaying a custom message
$message = __( "This is your custom message", "woocommerce" );
wc_add_notice( $message, 'error' );
// We stop the loop
break;
}
}
return $passed;
}
Code goes in function.php file of your active child theme (or active theme) or in any plugin file.
Tested and works.
I have been overriding the price of products dynamically based on specific criteria, but the ajax and mini-cart doesn't seem to see the price change when the product is getting added in the cart. It shows the original price total. I can override the prices in the cart itself no problem, but you have to be on the cart or checkout page to see it. Not sure what approach to take. I felt as though I've tried everything.
Seems as though $woocommerce->cart->get_cart_total() is called to display current cart total, but it doesn't seem to run add_action( 'woocommerce_before_calculate_totals', 'woo_add_discount'); hook when called.
If you go to the actual cart page, it is the proper pricing.
This code added will display the right individual price but not the subtotal. You have to refresh the cart page by clicking on the Cart URL for it to update the $woocommerce->cart->get_cart_total() object.
I've also tried add_action( 'woocommerce_before_mini_cart', 'woo_add_discount'); which does the same thing.. you have to refresh the page after loading. I'm sure I'm not the only one who had overriden prices and can't get all the peices to fall into place.
I've tried this and see that the second comment down on the answer, someone is having the same issue but no answers.
WooCommerce: Add product to cart with price override?
Try to use apply_filters instead of add_action
I ended up fixing this myself. I tried Paul de Koning's answer. Changing to apply_filters caused the prices to go to $0. I think this is because the order they need to go in.
Here was my solution. The root cause was that I was using the change price_html function to provide the change prices for the cart. Inside that function there were add_action calls etc. Somehow, there must have been an ordering issue. This is a multi step process to ensure all areas are done correctly.
If you want to change woocommerce prices without using sessions to store the changed price for the cart while changing the display price and hiding prices, perform something like the following:
functions.php
add_action('woocommerce_get_price_html','special_price');
function special_price($price) {
//put any if statements for hiding the cart button and discounting pricing below and return true or false
$displayprice = true;
$alterprice = true;
if($displayprice === true) {
add_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10, 2);
add_action( 'init', 'woocommerce_add_to_cart_action', 10);
add_action( 'init', 'woocommerce_checkout_action', 10 );
} else {
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10, 2);
remove_action( 'woocommerce_before_add_to_cart_form', 'woocommerce_template_single_product_add_to_cart', 10, 2); //
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
//return blank or something like 'Signup to view price'
return '';
}
//if you are displaying the price (altered)
if($displayprice === true && $alterprice === true){
$price = price_manipulator($theid);
return $price;
//display add to cart and price
}
//if you are displaying the price (unaltered)
return $price
}
function price_manipulator($theid = '') {
if(empty($theid)){
$theid = get_the_ID();
}
$product = wc_get_product($theid);
//30% off example
$newprice = floatval($product->price * (1-0.3));
return $newprice;
}
/*version of pricing if you are adding something to cart*/
function special_price_cart($theid){
$price = price_manipulator($theid);
return $price;
}
add_action( 'woocommerce_before_calculate_totals', 'woo_add_discount');
add_action( 'woocommerce_before_mini_cart', 'woo_add_discount'); //this is if you are using the mini-cart woocommerce widget
function woo_add_discount() {
global $woocommerce;
//create if statements same as if you were displaying the price
$displayprice = true;
if($displayprice === true){
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$price = special_price_cart($cart_item['data']->id);
$price = str_replace('$','',$price);
$price = str_replace(',','',$price);
if($price > 0){
$cart_item['data']->price = floatval($price);
}
}
}
}