简体   繁体   中英

WooCommerce - How can I add multiple, different products to cart using ajax with same add-to-cart-Button?

I am trying to use ajax to add multiple, different products to cart with one button click. The AddToCart-Button is living in a non-WooCommerce page template.

I think it can be solved by waiting for the first ajax post to succeed, then iterate to the next product and so on until all selected products are done ajax posting and after that finally the succeed should happen.

I just don't know how I do that :) Do you maybe know how?

This is my products form html:

<form id="buy-tickets" action="https://mydomain.de/cart/" class="cart" method="post" enctype="multipart/form-data" novalidate="">
<input type="hidden" name="product_id[]" value="5353"><div class="terminMain"><div class="terminDate">28.09.2019 Event Name 1<p><b>(Noch Plätze frei)</b></p></div><div class="personenNumber" id="personenNumber1">
                        <div class="inputField">
                            <input type="button" value="-" class="qtyminus" field="quantity_5353" id="qtyminus1">
                            <input type="text" id="quantity_5353" name="quantity_5353" value="0" class="qty">
                            <input type="button" value="+" class="qtyplus" field="quantity_5353" id="qtyplus1">
                            <input type="hidden" name="stock" value="1985" field="stock">
                        </div>
                    </div></div><input type="hidden" name="product_id[]" value="5354"><div class="terminMain"><div class="terminDate">31.03.2026 Event Name 2<p><b>(Noch Plätze frei)</b></p></div><div class="personenNumber" id="personenNumber2">
                        <div class="inputField">
                            <input type="button" value="-" class="qtyminus" field="quantity_5354" id="qtyminus2">
                            <input type="text" id="quantity_5354" name="quantity_5354" value="0" class="qty">
                            <input type="button" value="+" class="qtyplus" field="quantity_5354" id="qtyplus2">
                            <input type="hidden" name="stock" value="1989" field="stock">
                        </div>
                    </div></div>            <div class="buchenBtn">
                <div class="btns woocommerce add-to-cart"> 
                            <button type="submit" name="ticket_process" value="1" class="ajax_add_to_cart single_add_to_cart_button btn added" id="buchen-wootickets">Buchen</button> <a href="https://mydomain.de/cart/" class="added_to_cart wc-forward" title="Warenkorb anzeigen">Show Cart</a>

                </div>
            </div>
</form>

This is the jQuery:

(function ($) {
    $( document ).on( 'click', '.single_add_to_cart_button', function(e) {
        e.preventDefault();
        var tickets = $('#buy-tickets input[name="product_id[]"]');
        var ticketsData = [];
        var $this, ticketId, ticketQty, ticketProd;  
        tickets.each(function() {
                    $this = $( this );
                    id = $this.val();
                    ticketQty = $('#quantity_'+id).val();
                    if(ticketQty > 0){
                        ticketProd = {
                            action: 'woocommerce_ajax_add_to_cart', 
                            product_id: id, 
                            product_sku: '',
                            quantity: ticketQty,
                            variation_id: 0,
                        };
                        ticketsData.push(ticketProd);
                    }   
            });
        console.log('tickets: ', ticketsData);

        var $thisform = $('#buy-tickets');
        var $thisbutton = $(this);

        $.each(ticketsData, function(index, value){
            $.each(this, function (index, value) {
                console.log(index + " :: " + value);
            });
            $(document.body).trigger('adding_to_cart', [$thisbutton, this]);

            $.ajax({
                type: 'post',
                url: wc_add_to_cart_params.ajax_url,
                data: this,
                beforeSend: function (response) {
                    $thisform.addClass('loading');
                    $thisbutton.removeClass('added');
                },
                complete: function (response) {
                    $thisform.removeClass('loading');
                    $thisbutton.addClass('added');
                },
                success: function (response) {

                    if (response.error & response.product_url) {
                        window.location = response.product_url;
                        return;
                    } else {
                        $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $thisbutton]);
                    }
                },
            });

            return false;

        });
    });
})(jQuery);

And the php function handling my Ajax:

add_action( 'wp_ajax_woocommerce_ajax_add_to_cart', 'tec_woocommerce_ajax_add_to_cart' );
add_action( 'wp_ajax_nopriv_woocommerce_ajax_add_to_cart', 'tec_woocommerce_ajax_add_to_cart' );

function tec_woocommerce_ajax_add_to_cart() {

            $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
            $quantity          = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] );
            $variation_id      = absint( $_POST['variation_id'] );
            $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
            $product_status    = get_post_status( $product_id );

    if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id ) && 'publish' === $product_status ) {

        do_action( 'woocommerce_ajax_added_to_cart', $product_id );

        if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
            wc_add_to_cart_message( array( $product_id => $quantity ), true );
        }

        WC_AJAX::get_refreshed_fragments();
    } else {

        $data = array(
            'error'       => true,
            'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id ),
        );

        echo wp_send_json( $data );
    }

            wp_die();
}

The code is working already fine, only culprit: it merely adds 1 product-id and its set quantity to cart and stops after that, even though more than one product was selected by setting quantities for the other products as well.

I am looking for a solution, that will add all selected products to cart according to whatever qty is set per product.

Any help? suggestions?

The following code was added to my functions.php. It shows how to send multiple products to WooCommerce in a single AJAX request. It is just a skeleton you will need to add the PHP code that actually adds the products to the cart.

add_action( 'woocommerce_after_main_content', function() {
?>
<button id="saskia-add_multiple_products" style="background-color: cyan; border: 3px solid red; margin:50px;">
    Add Multiple Products
</button>
<script>
    jQuery( 'button#saskia-add_multiple_products' ).click( function() {
        var productsToAdd = [
            { id: 2650, quantity: 1 },
            { id: 3060, quantity: 2 }
        ];
        // There are multiple ways of sending complex data from JavaScript to WordPress
        // JavaScript: JSON.stringify(), jQuery: .serialize() or the following bare-bones
        // way which is wordy but probably the easiest to understand. Since, your data
        // is already in a HTML form .serialize() is probably the best but I am too lazy
        // to make a form so I am testing from data in a JavaScript array.
        var data = { action: 'saskia_add_multiple_products', id: [], quantity: [] };
        jQuery.each(productsToAdd, function( index, value ) {
            data.id[index]       = value.id;
            data.quantity[index] = value.quantity;
        } );
        jQuery.post( 'http://localhost/wp-admin/admin-ajax.php', data, function(data){ /* process success response */} );
    } );
</script>
<?php
} );

add_action( 'wp_ajax_saskia_add_multiple_products', function() {
    error_log( 'ACTION:wp_ajax_nopriv_saskia_add_multiple_products:$_POST=' . print_r( $_POST, TRUE ) );
    for ( $i = 0; $i < count( $_POST['id'] ); $i++ ) {
        $product_id = $_POST['id'][$i];
        $quantity   = $_POST['quantity'][$i];
        error_log( 'ACTION:wp_ajax_nopriv_saskia_add_multiple_products:$product_id=' . $product_id );
        error_log( 'ACTION:wp_ajax_nopriv_saskia_add_multiple_products:$quantity='   . $quantity );
        /* add each individual product to cart */
    }
    // return the update cart HTML fragments
    WC_AJAX::get_refreshed_fragments();
} );

The $_POST for this HTTP request is:

$_POST=Array
(
    [action] => saskia_add_multiple_products
    [id] => Array
        (
            [0] => 2650
            [1] => 3060
        )

    [quantity] => Array
        (
            [0] => 1
            [1] => 2
        )

)

You may want to try jQuery's .serialize() on your form. I think that will be the most concise solution. If this is not sufficient please let me know. Now that I have WooCommerce activated it is easy to add more details to this answer.

ADDENDUM

I tried jQuery's .serialize() on your HTML form. First you must add a hidden HTML <input> element to provide the AJAX action parameter:

<form id="buy-tickets" action="https://mydomain.de/cart/" class="cart" method="post" enctype="multipart/form-data" novalidate="">
    <input type="hidden" name="action" value="saskia_add_multiple_products" />
    ...
</form>

The jQuery AJAX call using .serialize() is:

jQuery.post( 'http://localhost/wp-admin/admin-ajax.php',
    jQuery('form#buy-tickets').serialize(),
    function(data) { /* process success response */}
);

This sends the following $_POST:

$_POST=Array
(
    [action] => saskia_add_multiple_products
    [product_id] => Array
        (
            [0] => 5353
            [1] => 5354
        )

    [quantity_5353] => 0
    [stock] => 1989
    [quantity_5354] => 0
)

I don't quite understand some of this but I think if you tweak your form you can get jQuery's .serialize() to batch your products. Consider changing

name="quantity_5353" -> name="quantity[]"
name="quantity_5354" -> name="quantity[]"

I don't think you need to send "stock". I would also not hard code the product data directly into the form but use PHP variables so the form can be used again with different products. Ideally the HTML for the products in the form should be generated using a foreach ( $products as $product ) { ... } so the form can work for any number of products not just 2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM