Woocommerce - Remove Only one Unit of a product from cart - php

I am working on this woocommerce search function that uses AJAX to fetch products on every keystroke and that gives the possibility to add or remove units of a certain product to cart right from the instant search results modal.
In each search result there is this group of two buttons that allow the client to add 1 or remove 1 product from cart at a time. I managed to make the add to cart button work but can't seem to find a good solution to remove just one single unit from cart instead of all of them at once.
I have tried this:
function remove_product_from_cart_programmatically() {
   if ( is_admin() ) return;
   $product_id = 282;
   $product_cart_id = WC()->cart->generate_cart_id( $product_id );
   $cart_item_key = WC()->cart->find_product_in_cart( $product_cart_id );
   if ( $cart_item_key ) WC()->cart->remove_cart_item( $cart_item_key );
}
but it removes all products with that ID at once.
I want to be able to remove unit by unit in case there are items in cart with that ID.
This is the code I have right now for the remove from cart button:
add_action('wp_ajax_search_remove_from_cart', 'search_remove_from_cart');
add_action('wp_ajax_nopriv_search_remove_from_cart', 'search_remove_from_cart');
// handle the ajax request
function search_remove_from_cart()
{
$product_id = $_REQUEST['product_id'];
WC()->cart->remove_cart_item($product_id);
$products_in_cart = WC()->cart->get_cart_item_quantities($product_id);
$updated_qty = $products_in_cart[$product_id];
// in the end, returns success json data
wp_send_json_success(["Product removed from cart Successfuly!", $updated_qty]);
// or, on error, return error json data
wp_send_json_error(["Could not remove the product from cart"]);
}
Is there any function to remove only one unit of a certain product from cart?
Thank you all !

You have to change the quantity instead of removing the product. Number of product units in the cart = quantity.
Try something like this:
function remove_from_cart(){
$product_id = 47;
$cart = WC()->cart;
$product_cart_id = $cart->generate_cart_id( $product_id );
$cart_item_key = $cart->find_product_in_cart( $product_cart_id );
if ( $cart_item_key ){
$quantity = $cart->get_cart_item($cart_item_key)['quantity'] - 1;
$cart->set_quantity($cart_item_key, $quantity);
}
}

Related

Changing WooCommerce coupon discount amount in cart

What I'm doing:
I am creating a coupon based promo. The coupon will discount 10% of the most expensive item in the cart that belongs to a specific product category.
The problem:
The code below successfully checks if the cart contains products from the specific category, and obtains 10% of the price of the most expensive product from that array to use for the discount. However, I can't get the coupon discount amount to change in the cart.
The code:
function change_coupon_price_for_promo( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ){
return;
}
// coupon code
$coupon_name = 'MYCOUPONNAME';
// if coupon is applied to cart...
if( $cart->has_discount( $coupon_name ) ) {
// Form array of products from the cart that belong to the specified category
$specProduct = array(); // array of specific products in cart
foreach ( $cart->get_cart() as $key => $cart_item ) {
$cart_item_id = $cart_item['product_id'];
$terms = wp_get_post_terms( $cart_item_id, 'product_cat' );
foreach ( $terms as $term ){
// gets product cat id
$product_cat_id = $term->term_id;
// gets an array of all parent category levels
$product_parent_categories_all_hierachy = get_ancestors( $product_cat_id, 'product_cat' );
// This cuts the array and extracts the last set in the array
$last_parent_cat = array_slice($product_parent_categories_all_hierachy, -1, 1, true);
if(in_array(3831, $last_parent_cat)){
$specProduct[$key] = $cart_item_id;
break;
}
}
}
//if there are items from the specific category in the cart
if(!empty($specProduct)){
$specPriceArray = array(); // array of specific product prices in cart
foreach ($specProduct as $key => $specItem) {
$thisProd = wc_get_product($specItem);
$thisPrice = $thisProd->get_price();
$specPriceArray[$key] = $thisPrice;
}
// most expensive specific product price
$highestSpec = max($specPriceArray);
// discount of most expensive item
$finalDiscount = $highestSpec * .1;
// print_r($specProduct); GOOD
// print_r($specPriceArray); GOOD
// echo $highestSpec; GOOD
// echo $finalDiscount; GOOD
/***** APPLY COUPON CHANGE HERE<-------HOW??? *****/
$cart->discount_total = $finalDiscount;
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'change_coupon_price_for_promo', 10, 1 );
What I'm asking:
The last line of the code before the closing brackets...
$cart->discount_total = $finalDiscount;
is what I tried using to change the coupon's discount in the cart. It doesn't work. How can I accomplish this? TY.
EDIT:
I should note that the coupon being added is set up as a "Fixed cart discount" with a rate of 0 so it is added as a line item to the order. This way the user sees the discount for the coupon code on its own line in the cart/checkout.
you can use function set_price.
you can call it in your code like:
$cart_item['data']->set_price('New price here')
use this inside the foreach that loops your cart items

Programmatically removing a product from basket

I'm using the following code to add a free product to the basket when over a certain cart total.
When I drop below the cart total it hits the if statement but won't remove the product.
I think this may be because the quantity is set to 1 on the basket form and my code isn't overriding that quantity to set it to 0 (and remove it).
Is there another way to remove it or override it?
/*
* Automatically adding the product to the cart when cart total amount reach to £20.
*/
function aapc_add_product_to_cart() {
global $woocommerce;
$cart_total = 20;
$free_product_id = 85028; // Product Id of the free product which will get added to cart
if ( $woocommerce->cart->total >= $cart_total ) {
echo "Over the limit";
$quantity = 1;
WC()->cart->add_to_cart( $free_product_id, $quantity );
} elseif($woocommerce->cart->total < $cart_total) {
echo "Under the limit";
$quantity = 0;
WC()->cart->remove_cart_item( $free_product_id, $quantity );
}
}
add_action( 'template_redirect', 'aapc_add_product_to_cart' );
I've tried using this question but cannot get it to work, it won't even add the item to my basket.
I'm using Woocommerce version 3.6.5 if that helps.
I'm not sure on how Woocommerce works but I assume the remove_cart_item 'quantity' parameter takes the amount of the item to remove from the basket. So set the value of $quantity to 1 and it should remove one of the free product from the basket. If there happens to be more than one free item in the basket then you'll need to set your $quantity value accordingly.
Furthermore, you are adding a free item every time a product is added when the basket value is higher than $cart_total. Maybe add a check to see if it already exists in the basket before adding it?
Hope I understood your question correctly
/*
* Automatically adding the product to the cart when cart total amount reach to £20.
*/
function aapc_add_product_to_cart() {
global $woocommerce;
$cart_total = 20;
$free_product_id = 85028; // Product Id of the free product which will get added to cart
if ( WC()->cart->total >= $cart_total ) {
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $free_product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $free_product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $free_product_id );
}
} elseif(WC()->cart->total < $cart_total) {
$quantity = 0;
$prod_unique_id = WC()->cart->generate_cart_id( $free_product_id );
// Remove it from the cart by un-setting it
unset( WC()->cart->cart_contents[$prod_unique_id] );
WC()->cart->remove_cart_item( $free_product_id );
}
}
add_action( 'woocommerce_after_calculate_totals', 'aapc_add_product_to_cart' );

Remove conditionally Woocommerce cart items based on a specific product

A specific WooCommerce product can only be by itself in the cart.
So how do I clear the cart while adding this specific product to the cart? and how can I remove this specific product from the cart when adding any other product?
I've figured out how to empty the cart when adding a specific product but I don't know how to remove this specific product from the cart when adding any other product.
The following will remove conditionally cart items based on a specific product:
When the specific product is added to cart, all other items are removed.
When any other product is added to cart, it removes the specific product (if it's in cart)
Here is the code:
// Remove conditionally cart items based on a specific product (item)
add_action( 'woocommerce_before_calculate_totals', 'remove_cart_items_conditionally', 10, 1 );
function remove_cart_items_conditionally( $cart ) {
// HERE define your specific product ID
$specific_product_id = 37;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$cart_items = $cart->get_cart(); // Cart items array
$items_count = count($cart_items); // Different cart items count
// Continue if cart has at least 2 different cart items
if ( $items_count < 2 )
return;
$last_item = end($cart_items); // Last cart item data array
$is_last_item = false; // Initializing
// Check if the specific product is the last added item
if ( in_array($specific_product_id, array( $last_item['product_id'], $last_item['variation_id'] ) ) ) {
$is_last_item = true;
}
// Loop through cart items
foreach ( $cart_items as $cart_item_key => $cart_item ) {
// Remove all others cart items when specific product ID is the last added to cart
if ( ! in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
// Remove the specific item when its is not the last added to cart
elseif ( in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && ! $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
}
}
Code goes on function.php file of your active child theme (or active theme). Tested and works.
The above works perfectly! You can also add a notice to the cart once items have been removed to help with the user experience. Add this to the // Loop through cart items section in the first if statement:
wc_add_notice( __( 'Product XYZ has been removed from your cart because...', 'theme_domain' ), 'notice' );

Set a custom cart item price value from a GET variable In Woocommerce 3

I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.
I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.
However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).
Here's that code:
add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);
function pay_custom_amount() {
$payment_value = $_GET['amount'];
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 21 ){
$cart_item['data']->set_price($payment_value);
}
}
}
Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.
If anyone know what might be happening here, it would be great if you could share.
You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.
In the below code:
the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.
Now your the target product ID in your code need to be the same ID that the added to cart product.
The code:
// Get the custom "amount" from URL and save it as custom data to the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_GET['amount']) )
return $cart_item_data;
$amount = esc_attr( $_GET['amount'] );
if( empty($amount) )
return $cart_item_data;
// Set the custom amount in cart object
$cart_item_data['custom_price'] = (float) $amount;
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Alter conditionally cart item price based on product ID and custom registered "amount"
add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
function change_conditionally_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your targeted product ID
$targeted_product_id = 21;
foreach ( $cart->get_cart() as $cart_item ) {
// Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Woocommerce: Programmatically Update Item In Cart

I need to programmatically and dynamically change the price of an item in the cart.
I’ve tried varying combinations of Woocommerce action hooks, the cart and session objects, but nothing quite seems to do the trick. I thought this wouldn’t be so challenging.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( 123 == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
}
}
}
The above code changes the price for each item only on the checkout page, or when updating the cart (ie: the hook is called when removing an item from the cart), but not indefinitely.
I'm using the Woocommerce Gravity Forms add-on. I have one product in particular, which will be ordered multiple times by a given user. The user will be allowed 5x free with only shipping fees, and each above 5 will be $20. I have this much coded and functional with Gravity Forms hooks that dynamically populate fields. Shipping is specific to fields within the gravity form, therefore I am leaving that calculation to Gravity Forms.
My issue is that if a user reduces the quantity of this product from their order (removes one of the items from the cart), it should re-calculate the price of each item of the same product within the cart, otherwise they could be over-charged (an item that used to be the 6th is now the 4th, but the price remains the same, which it shouldn't)
Therefore, I would like to re-calculate the price of each item in the cart, based on the quantity of this particular product, every time something is removed from the cart.
--- EDIT ---
The above code works, but I'm realizing the issue must be a custom loop I'm using to display the prices...
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$price_not_updated = $cart_item['data']->price;
}
}
I figured it out... I looked at the woocommerce cart docs and essentially realized that the prices I was getting had yet to be calculated. So, before running the loop, I had to do the action that I was initially hooking into to change the prices.
Thanks for your help!
function getUpdatedCartPrices() {
do_action( 'woocommerce_before_calculate_totals', WC()->cart );
$horray_updated_prices_works = array();
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$horray_updated_prices_works[] = $cart_item['data']->price;
}
}
}
I changed the function to have a second parameter so that you can use it dynamically with any product as long as you call the proper id. I also set the function return value to either a success or error message. This way you can know if it was changed, and if so, to what price. Hope this helps.
by the way, having this function called again when the cart is updated should solve the recalculation issue. It would be great if i could see the code for your $function_to_get_new_price() function.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price($id, $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( $id == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
$successMsg = "The price of item $value['data']->id was set to $value['data']->price".;
return $successMsg;
}else{
$errorMsg = "Price was not changed!";
return $errorMsg;
}
}
}

Categories