skip to Main Content

I use latest version of WordPress and Woocommerce. I have some JavaScript that loops over DOM elements of class .selectable and grabs the product_id attribute from the ones with class .selected, and adds that item to the cart:

// selects the elements
$('.next').on('click', function() {
    var parent  = $(this).closest('.panel');
    var selectables = parent.children('.option.selectable.selected');

    $(selectables).each(function() {
        var product = $(this).attr('product');
        add_to_cart(product, 1);    
    });
}

// add to cart AJAX
function add_to_cart(product_id, qty) {
    $.ajax({
        type: "POST",
        url: base_url + '/wp-admin/admin-ajax.php',
        data: {action : 'cl_add_to_cart', 'product_id': product_id, 'qty': qty},
        success : function(response) {
            console.log(response);
        }
    });
}

And I have a PHP function in functions.php

add_action('wp_ajax_cl_add_to_cart', 'cl_add_to_cart');
add_action('wp_ajax_nopriv_cl_add_to_cart', 'cl_add_to_cart');

function cl_add_to_cart() {
    global $woocommerce;
    $product_id   = trim($_POST['product_id']);
    $qty = !empty($_POST['qty']) ? trim($_POST['qty']) : 1;
    $added = WC()->cart->add_to_cart($product_id, $qty);
    echo json_encode($added);
}

The AJAX call is hitting the PHP function just fine – I can echo out $added which gives me a hash. If I have two or more items in the selectables var, they both hit the PHP function (I get two hashes back) but only the first one gets added to the cart.

I can’t for the life of me figure out why. Any ideas?

2

Answers


  1. Is better to send all items to be added all together at once with Ajax, than send them separately in multiple Ajax requests, this will be lighter and more effective.

    You need to be sure that $(selectables).each(function() { // ... }); is working as you expect.

    The following will send a unique Ajax request for your click event.

    The jQuery code

    // Multi Ajax adda to cart
    function multiAddToCart( productsData ) {
        $.ajax({
            url: base_url + '/wp-admin/admin-ajax.php',
            type: 'POST',
            dataType: 'JSON',
            data: {
                'action': 'multi_add_to_cart',
                'items' : Object.assign({}, productsData)
            },
            success : function(response) {
                $(document.body).trigger('wc_fragment_refresh'); // Refresh cart fragments
                console.log(response);
            },
            error : function(error) {
                console.log(error);
            }
        });
    }
    
    $('.next').on('click', function() {
        var productsData = [];
        var parent  = $(this).closest('.panel');
        var selectables = parent.children('.option.selectable.selected');
    
        $(selectables).each(function() {
            var productId = $(this).attr('product');
            productsData.push({'id': productId, 'qty': 1});
        });
        // Check the data before (number of items found)
        console.log(productsData); 
    
        multiAddToCart( productsData );
    });
    

    The php receiver function:

    add_action('wp_ajax_multi_add_to_cart', 'multi_ajax_add_to_cart');
    add_action('wp_ajax_nopriv_multi_add_to_cart', 'multi_ajax_add_to_cart');
    function multi_ajax_add_to_cart() {
        if (isset($_POST['items']) ) {
            $item_keys = array();
    
            foreach( $_POST['items'] as $item ) {
                if( isset($item['id']) ) {
                    $item_qty  = isset($item['qty']) && $item['qty'] > 1 ? $item['qty'] : 1;
                    $item_keys[] = WC()->cart->add_to_cart($item['id'], $item_qty);
                }
            }
            echo json_encode($item_keys); // Send back cart item keys
        }
        die();
    }
    

    Code goes in functions.php file of the active child theme (or active theme). Tested and works.

    It should work adding all items at once.

    Login or Signup to reply.
  2. The main answer does not really address the underlying problem. Plus it is not always possible to add the products at the same time. You may have an order form where the user can click quickly on multiple different items.

    Here’s the problem:
    The WooCommerce add to cart function works by setting the $this->cart_contents variable to specific items.

    The problem occurs when you use WC()->cart->add_to_cart() in AJAX, because multiple such add_to_cart functions are running concurrently. What happens is that one function will start after the previous function started but did not finish.

    The result is that the 2nd function sets the cart content, without knowing it’s missing the products the previous function just added.

    Now the solution: Make the javascript wait until the previous function finished with a recursive function:

    (this code is not complete, it just illustrates the idea):

    add_cart_clear = 'yes';
    
    function order_form_add(datavar, qty){
    
        if (add_cart_clear === 'yes'){ // proceed only if clear
            
            add_cart_clear = 'no'; // new function started, not clear
            $.post(ajaxurl, data, function(response){ // function ended, it's clear again
                add_cart_clear = 'yes';                             
            });
    
        } else {
    
            setTimeout(function(){
                order_form_add(data, qty); // if not ready, wait 100ms and try again
            }, 100);
        }
        
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search