WooCommerce - automatic update of the total price when the quantity changes

I searched for a few days, but there is no answer yet. Basically, I am trying to replace the standard woocommerce "Refresh Cart" button with an ajax call that automatically updates the total cost of the order when the quantity changes. This is my html

<div class="cart_item"> <div class="product-thumbnail"> <a href="http://example.com"><img width="90" height="90" src="path to thumbnail"/></a> </div> <div class="product-name"> <a class="cart-page-product__title" href="http://example.com">Product1 name</a> </div> <div class="product-quantity"> <div class="quantity"> <input type="number" step="1" name="cart[some_security_key][qty]" value="1" class="input-text qty text" size="4"/> </div> </div> <div class="product-subtotal"><span class="amount">2 000</span></div> <div class="product-remove"> <a class="product-remove_link" href="http://example.com/?remove_item">&times;</a> </div> </div> <div class="cart_item"> <div class="product-thumbnail"> <a href="http://example.com"><img width="90" height="90" src="path to thumbnail"/></a> </div> <div class="product-name"> <a class="cart-page-product__title" href="http://example.com">Product2 name</a> </div> <div class="product-quantity"> <div class="quantity"> <input type="number" step="1" name="cart[some_security_key][qty]" value="1" class="input-text qty text" size="4"/> </div> </div> <div class="product-subtotal"><span class="amount">2 000</span></div> <div class="product-remove"> <a class="product-remove_link" href="http://example.com/?remove_item">&times;</a> </div> </div> 

In functions.php, I have a function to update totals:

 public function update_total_price() { check_ajax_referer( 'update_total_price', 'security' ); if ( ! defined('WOOCOMMERCE_CART') ) { define( 'WOOCOMMERCE_CART', true ); } $cart_updated = false; $cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : ''; if ( sizeof( WC()->cart->get_cart() ) > 0 ) { foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { $_product = $values['data']; // Skip product if no updated quantity was posted if ( ! isset( $_POST['quantity'] ) ) { // if ( ! isset( $cart_totals[ $cart_item_key ]['qty'] ) ) { continue; } // Sanitize $quantity = apply_filters( 'woocommerce_stock_amount_cart_item', apply_filters( 'woocommerce_stock_amount', preg_replace( "/[^0-9\.]/", '', filter_var($_POST['quantity'], FILTER_SANITIZE_NUMBER_INT)) ), $cart_item_key ); // $quantity = apply_filters( 'woocommerce_stock_amount_cart_item', apply_filters( 'woocommerce_stock_amount', preg_replace( "/[^0-9\.]/", '', $cart_totals[ $cart_item_key ]['qty'] ) ), $cart_item_key ); if ( '' === $quantity || $quantity == $values['quantity'] ) continue; // Update cart validation $passed_validation = apply_filters( 'woocommerce_update_cart_validation', true, $cart_item_key, $values, $quantity ); // is_sold_individually if ( $_product->is_sold_individually() && $quantity > 1 ) { wc_add_notice( sprintf( __( 'You can only have 1 %s in your cart.', 'woocommerce' ), $_product->get_title() ), 'error' ); $passed_validation = false; } if ( $passed_validation ) { WC()->cart->set_quantity( $cart_item_key, $quantity, false ); } $cart_updated = true; } } // Trigger action - let 3rd parties update the cart if they need to and update the $cart_updated variable $cart_updated = apply_filters( 'woocommerce_update_cart_action_cart_updated', $cart_updated ); if ( $cart_updated ) { // Recalc our totals WC()->cart->calculate_totals(); woocommerce_cart_totals(); exit; } } 

And the jQuery code:

 jQuery( function( $ ) { // wc_cart_params is required to continue, ensure the object exists if ( typeof wc_cart_params === 'undefined' ) { return false; } // Cart price update depends on quantity //$( document ).on( 'click', '.quantity', function() { $( document ).on( 'change', '.quantity, input[type=number]', function() { var qty = $( this ).val(); var currentVal = parseFloat( qty); $( 'div.cart_totals' ).block({ message: null, overlayCSS: { background: '#fff url(' + wc_cart_params.ajax_loader_url + ') no-repeat center', backgroundSize: '16px 16px', opacity: 0.6 } }); var data = { action: 'rf_update_total_price', security: rf_cart_params.rf_update_total_price_nonce, quantity: currentVal }; $.post( rf_cart_params.ajax_url, data, function( response ) { $( 'div.cart_totals' ).replaceWith( response ); $( 'body' ).trigger( 'rf_update_total_price' ); }); return false; }); }); 

The code above works great if there is only one product in the cart. enter image description here

But when I add some other product and change the quantity of one of them, my function uses the last quantity value for all my products.

enter image description here

For example, the total cost of the second image should be 7000 (2000 * 1 + 2500 * 2), but this is 9000 (2000 * 2 + 2500 * 2). I am new to ajax and jquery, so appreciate any help.

+7
jquery php wordpress woocommerce
source share
3 answers

This is because you are updating your entire basket, not just the product.

First you need to send the hash of the cart item (this is not a security hash, but a product hash with all the product changes) to a javascript script:

 var item_hash = $( this ).attr( 'name' ).replace(/cart\[([\w]+)\]\[qty\]/g, "$1"); var data = { action: 'rf_update_total_price', security: rf_cart_params.rf_update_total_price_nonce, quantity: currentVal, hash : item_hash }; 

Then you can edit your function update_total_price , I simplified;)

 function update_total_price() { // Skip product if no updated quantity was posted or no hash on WC_Cart if( !isset( $_POST['hash'] ) || !isset( $_POST['quantity'] ) ){ exit; } $cart_item_key = $_POST['hash']; if( !isset( WC()->cart->get_cart()[ $cart_item_key ] ) ){ exit; } $values = WC()->cart->get_cart()[ $cart_item_key ]; $_product = $values['data']; // Sanitize $quantity = apply_filters( 'woocommerce_stock_amount_cart_item', apply_filters( 'woocommerce_stock_amount', preg_replace( "/[^0-9\.]/", '', filter_var($_POST['quantity'], FILTER_SANITIZE_NUMBER_INT)) ), $cart_item_key ); if ( '' === $quantity || $quantity == $values['quantity'] ) exit; // Update cart validation $passed_validation = apply_filters( 'woocommerce_update_cart_validation', true, $cart_item_key, $values, $quantity ); // is_sold_individually if ( $_product->is_sold_individually() && $quantity > 1 ) { wc_add_notice( sprintf( __( 'You can only have 1 %s in your cart.', 'woocommerce' ), $_product->get_title() ), 'error' ); $passed_validation = false; } if ( $passed_validation ) { WC()->cart->set_quantity( $cart_item_key, $quantity, false ); } // Recalc our totals WC()->cart->calculate_totals(); woocommerce_cart_totals(); exit; } 
+5
source share

Here's an easier way to achieve this. All loans relate to Reigel Gallarde .

 // we are going to hook this on priority 31, so that it would display below add to cart button. add_action( 'woocommerce_single_product_summary', 'woocommerce_total_product_price', 31 ); function woocommerce_total_product_price() { global $woocommerce, $product; // let setup our divs echo sprintf('<div id="product_total_price" style="margin-bottom:20px;display:none">%s %s</div>',__('Product Total:','woocommerce'),'<span class="price">'.$product->get_price().'</span>'); echo sprintf('<div id="cart_total_price" style="margin-bottom:20px;display:none">%s %s</div>',__('Cart Total:','woocommerce'),'<span class="price">'.$product->get_price().'</span>'); ?> <script> jQuery(function($){ var price = <?php echo $product->get_price(); ?>, current_cart_total = <?php echo $woocommerce->cart->cart_contents_total; ?>, currency = '<?php echo get_woocommerce_currency_symbol(); ?>'; $('[name=quantity]').change(function(){ if (!(this.value < 1)) { var product_total = parseFloat(price * this.value), cart_total = parseFloat(product_total + current_cart_total); $('#product_total_price .price').html( currency + product_total.toFixed(2)); $('#cart_total_price .price').html( currency + cart_total.toFixed(2)); } $('#product_total_price,#cart_total_price').toggle(!(this.value <= 1)); }); }); </script> <?php } 
+3
source share

Starting with WooCommerce 2.6.0, released in June 2016, the WooCommerce shopping cart page uses Ajax to update the basket totals after clicking the Update Cart button. WooCommerce 2.6.0 requires WP 4.4 or later.

No need to worry about the backend and creating your own Ajax call, you can use the one assigned to the Update Cart button.

To get this functionality out of the box, you can use my free plugin, which also has several convenient additional options:

Ajax Shopping Cart Auto Update for WooCommerce

Or use a child theme to do the following. Hide the "Refresh Cart" button, and then queue a script that fires the default cart refresh event when the number of cart pages changes (works for both the cart and the mini-cart). Use template redirection, a dependency with jQuery and make sure that this script only loads on the cart page.

To hide the button with CSS, use the .button class instead of the tag so that it is compatible with all versions of WooCommerce:

 .button[name='update_cart'] { display: none!important; } 

Or hide the PHP head style button:

 add_action('wp_head', 'hide_update_cart_button', 20); function hide_update_cart_button() { echo "<style>.button[name='update_cart']{ display: none!important;}</style>"; } 

Make the script dependent on jQuery:

 add_action( 'template_redirect', 'auto_update_cart_totals' ); function auto_update_cart_totals() { if (! is_cart() ) return; // Only if it cart page. // Enqueue js file. add_action( 'wp_enqueue_scripts', 'my_cart_autoupdate' ); } function my_cart_autoupdate( ) { // Here goes code to hide CSS button if you decide to use PHP solution for styling. wp_enqueue_script( 'my-cart-autoupdate', 'path-to-js-file-its-name-and-extension', array('jquery'), '', true); } 

The most important and usually overlooked thing in jQuery code is to set the update delay, 1000 in this example, my plugin allows me to change this value in the settings. If the user changes the quantity again during this delay, it will be reset to its full duration. If this is not implemented and you change the quantity from 1 to 10 by clicking on the increase button, this will cause 9 Ajax calls instead of 1. Put this in the .js file:

 var timeout; jQuery('div.woocommerce').on('change keyup mouseup', 'input.qty', function(){ // keyup and mouseup for Firefox support if (timeout != undefined) clearTimeout(timeout); //cancel previously scheduled event if (jQuery(this).val() == '') return; //qty empty, instead of removing item from cart, do nothing timeout = setTimeout(function() { jQuery('[name="update_cart"]').trigger('click'); }, 1000 ); }); 
0
source share

All Articles