php WooCommerce - 数量变化时自动更新总价

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26137156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 18:10:28  来源:igfitidea点击:

WooCommerce - auto update total price when quantity changed

phpjquerywordpresswoocommerce

提问by grimasa

I've been searching for several days but I have no answer yet. Basically, I'm trying to replace woocommerce standart "update cart" button with ajax call, which automatically updates order total price when quantity changed. This is my html

我已经找了好几天了,但我还没有答案。基本上,我正在尝试用 ajax 调用替换 woocommerce 标准的“更新购物车”按钮,当数量发生变化时,它会自动更新订单总价。这是我的 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 for updating totals:

在functions.php中,我有一个更新总数的函数:

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 Jquery code is:

而 Jquery 代码是:

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 above code works great if only one product is in the cart. enter image description here

如果购物车中只有一种产品,则上述代码效果很好。 在此处输入图片说明

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

但是,当我添加一些其他产品并更改其中一个产品的数量时,我的功能会使用我所有产品的最后数量值。

enter image description here

在此处输入图片说明

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

例如,第二张图片的总价必须是 7000(2000*1+2500*2) 但它是 9000(2000*2+2500*2)。我是 ajax 和 jquery 的新手,所以感谢任何帮助。

采纳答案by XciD

It's because you are updating all your cart, not just a product.

这是因为您要更新所有购物车,而不仅仅是产品。

First you need to send the item cart hash (it's not a security hash, but the product hash with all the product variation) on the javascript script:

首先,您需要在 javascript 脚本上发送商品购物车哈希(它不是安全哈希,而是包含所有产品变体的产品哈希):

var item_hash = $( this ).attr( 'name' ).replace(/cart\[([\w]+)\]\[qty\]/g, "");
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've simplified ;)

然后你可以编辑你的function update_total_price,我已经简化了;)

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;
}

回答by adamj

Here's a simpler way of achieving this. All credit goes to Reigel Gallarde.

这是实现此目的的更简单方法。所有功劳都归功于 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's 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
}

回答by Ryszard J?draszyk

Since WooCommerce 2.6.0, released June 2016, WooCommerce cart page uses Ajax to update cart totals after clicking on Update cart button. WooCommerce 2.6.0 requires WP 4.4 or newer.

自 2016 年 6 月发布的 WooCommerce 2.6.0 以来,WooCommerce 购物车页面在单击“更新购物车”按钮后使用 Ajax 更新购物车总数。WooCommerce 2.6.0 需要 WP 4.4 或更新版本。

No need to worry about backend and creating your own Ajax call, the one assigned to Update cart button can be used.

无需担心后端和创建您自己的 Ajax 调用,可以使用分配给“更新购物车”按钮的那个。

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

要立即使用此功能,您可以使用我的免费插件,它还有一些方便的附加选项:

Ajax Cart AutoUpdate for WooCommerce

WooCommerce 的 Ajax 购物车自动更新

Or use child theme to do the following. Hide Update cart button and then enqueue script which triggers default update cart event on cart page quantity change (works both for cart and mini-cart widget). Use template redirect hook, dependency with jQuery and make sure that this script loads only on cart page.

或使用子主题执行以下操作。隐藏更新购物车按钮,然后将在购物车页面数量更改时触发默认更新购物车事件的脚本加入队列(适用于购物车和迷你购物车小部件)。使用模板重定向钩子,依赖 jQuery 并确保此脚本仅在购物车页面上加载。

To hide button with CSS, use class .button instead of tag, to keep it compatible with all WooCommerce versions:

要使用 CSS 隐藏按钮,请使用 class .button 而不是 tag,以使其与所有 WooCommerce 版本兼容:

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

Or hide button with PHP head style:

或者隐藏带有 PHP 头部样式的按钮:

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

Enqueue script in dependency with jQuery:

依赖于 jQuery 的排队脚本:

add_action( 'template_redirect', 'auto_update_cart_totals' );
function auto_update_cart_totals() {    
    if (! is_cart() ) return; // Only if it's 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 about jQuery code is to set update delay, 1000 in this example, my plugin allows to change this value in settings. If user changes quantity again during this delay, it will be reset to full duration. If it's not implemented and you change quantity from 1 to 10 by clicking on increment button, it will trigger 9 Ajax calls instead of 1. Place this in .js file:

关于 jQuery 代码最重要但通常被忽视的事情是设置更新延迟,在这个例子中是 1000,我的插件允许在设置中更改这个值。如果用户在此延迟期间再次更改数量,它将重置为完整持续时间。如果它没有实现并且您通过单击增量按钮将数量从 1 更改为 10,它将触发 9 个 Ajax 调用而不是 1 个。将其放在 .js 文件中:

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 );
});