This post is a bit over­due con­sid­er­ing the project for which I did this was com­pleted months ago. I finally got around to doc­u­ment­ing it. Magento’s prod­uct list view lets cus­tomers add prod­ucts to the shop­ping cart one at a time. The client wanted cus­tomers to be able to add mul­ti­ple prod­ucts to the shop­ping cart simul­ta­ne­ously. Given the time con­straints for the projects, I cre­ated an ad hoc AJAX method to accom­plish this fea­ture request.

Adding a prod­uct to a Magento (ver. 1.3.1) shop­ping cart is accom­plished through an HTTP GET request. It will look like or sim­i­lar to this:

/path/to/app/checkout/cart/add?product=[id]&qty=[qty]

That URL is out­put by the tem­plate helper:

$this->getAddToCartUrl($_product)

Since adding a prod­uct to the shop­ping cart is noth­ing more than a GET request, then all that needs to be done is queue up the URLs of the desired prod­ucts, make each request in order, and then reload the page when done.

First, in app/design/frontend/ / /template/catalog/product/list.html, I first added check­boxes to allow cus­tomers to select which prod­ucts they want and also hid­den fields for stor­ing the URLs to add the prod­uct and text fields for the quantity. 

<input type="checkbox" class="input-checkbox add" name="add_<?php echo $_iterator; ?>" id="add_<?php echo $_iterator; ?>" />
<input type="hidden" name="url_<?php echo $_iterator; ?>" id="url_<?php echo $_iterator; ?>" value="<?php echo $this->getAddToCartUrl($_product) ?>" />
<?php if(!$_product->isGrouped()): ?>
     <input type="text" class="input-text qty" name="qty_<?php echo $_iterator; ?>" id="qty_<?php echo $_iterator; ?>" maxlength="12" value="<?php echo $this->getMinimalQty($_product) ?>" />
<?php endif; ?>

I added this code within the loop that gen­er­ate the HTML for the prod­uct line items for the list. Next, I added the JavaScript that does the actual pro­cess­ing, also within the list sec­tion right after the script block that con­tains: decorateList('products-list', 'none-recursive').

<script type="text/javascript">
    function processNext(urls, i) {
        var next = i + 1;
        $('processing-text').update('Processing item ' + next);
        if (next < urls.size()) {
            new Ajax.Request(urls[i], {
              method: 'get',
              onComplete: function(transport) {
                processNext(urls, next);
              }
            });
        } else {
            new Ajax.Request(urls[i], {
              method: 'get',
              onComplete: function(transport) {
                window.location.reload();
              }
            });
        }
    }

    function addItemsToCart() {
        $('add-items-to-cart').hide();
        $('waiting').show();

        var addToCartUrls = [];
        $$('input.add').each(function(e){
            if(e.checked == true) {
                var id = e.readAttribute('id').split('_')[1];
                var qty = Number($('qty_' + id).value);
                if (qty < 1) qty = 1;
                addToCartUrls.push($('url_' + id).value + 'qty/' + qty);
            }
        });

        if (addToCartUrls.size() > 0) {
            processNext(addToCartUrls, 0);
        } else {
            $('add-items-to-cart').show();
            $('waiting').hide();
            alert('Please check off the items you want to add.');
        }
    }
</script>

At the bot­tom of the list I added a sub­mit button.

<div style="margin-bottom:5px; text-align:right;"><button id="add-items-to-cart" class="form-button" onclick="addItemsToCart()"><span><?php echo $this->__('Add Items to Cart') ?></span></button><div id="waiting" style="height:22px; display:none; line-height:22px;"><span id="processing-text">Processing...</span> <img src="<?php echo $this->getSkinUrl().'images/wait22trans.gif'; ?>" width="22" height="22" style="display:inline; vertical-align:middle;"/></div></div>

Click­ing the but­ton calls the func­tion addItemsToCart(). The func­tion hides the but­ton to pre­vent a dou­ble click and unhides the sta­tus mes­sage. Next, the func­tion deter­mines which check­boxes are checked. For each checked check­box, the func­tion finds the cor­re­spond­ing URL field and quan­tity field, con­cate­nates the two val­ues, and stores the new URL in an array. If the length of the array is greater than 0, then the func­tion calls processNext(), oth­er­wise it dis­plays an error mes­sage and resets the sub­mit button.

The func­tion processNext() first updates the pro­cess­ing mes­sage. The func­tion takes the array of URLs and an index, and then cre­ates an AJAX GET request using the URL at the given index in the array. If the AJAX request com­pletes, it calls process­Next() with the same array but with an incre­mented index while the index is less than the array length. If the incre­mented index is greater than the array length, then that ends the pro­cess­ing and the func­tion reloads the page.

That’s it. If there is any­thing wrong with the code, it assumes that all the GET requests will com­plete. Unfor­tu­nately, given the time con­straints there was no time to account for the sce­nario where a GET request fails.

Leave a Reply