Magento Checkout: Shopping Cart Flow

POSTED ON Dec 12, 2012 IN in How To, Overview

Magento Checkout: Shopping Cart Flow

Shopping Cart is a starting point for every customer in the checkout flow. That is why the article about it, is the next one in “Magento Checkout from A to Z” series. In this article you will learn what happens during viewing shopping cart page, adding product to cart or changing products’ qty. As well you will get familiar with some customizations principles of that functionality. So get some pop-corn, take your favorite drink and have fun!

Shopping Cart Processes

There are some main processes in the shopping cart that is quite good to know. The following flowcharts and their descriptions will help you.

Adding Product to Cart

Everything that starts in checkout – starts from adding product to cart. As soon as customer presses “Add to Cart” button – Magento magic begins, and today this magic secrets will be revealed. Take a lot at the following diagram to see the main processes flow. Just below the diagram see each process description. Click on diagram to view it in the full size.

1. Controller Loads Product

This step starts at checkout/cart/add controller action, for which Mage_Checkout_CartController class is responsible. Controller loads product by id passed via product url parameter. If the product is not loaded (not exist, not enabled, etc), then it returns customer to the previous page. Previous page is detected by referrer header or parameter.If it is not possible to detect previous page, customer just gets redirected to cart page. The loaded product with url parameters are passed to cart model method addProduct().

2. Cart Model Prepares Product Request

Cart model is represented by Mage_Checkout_Model_CartInside of addProduct() method first product model gets checked for assignment to the current website. After the check, from url params, gets created a product request object. The request object just a Varien_Object class instance with url params as data. After creation of the object, requested quantity gets validated for matching minimal qty settings of the product. If qty is lower, it gets corrected automatically. When product and request is prepared, the quote model method addProduct()gets involved.

3. Quote Model Processes Product Request

Finally Magento gets to a quote model, but just to pass request to product type model. At this step, in addProduct() method of Mage_Sales_Model_Quote class invoked prepareForCartAdvanced()of product type model.

4. Product Type Model Validation and Processing of Request

Depends on the product type, there could be different product type models. These type models are responsible for product type specific logic. For instance simple product is just contains logic for custom options, configurable has special child product selection logic, bundle has multiple children, etc. However all product type models are extended from Mage_Catalog_Model_Product_Type_Abstract class. By the way, before 1.5, there was different method name, that were invoked during add to cart process. In 1.4.x it was prepareForCart(), and starting from 1.5.0 the method was changed to prepareForCartAdvanced() and logic were moved to a protected method _prepareProduct() with 3 parameters The reason of this change is very simple, they introduced third argument and for keeping backward compatibility, they were supposed to create another method and keep prepareForCart() as alias with two arguments. So currently all the logic of processing & validation of product request is in _prepareProduct() protected method and it is different per product type.

Simple Products (Mage_Catalog_Model_Product_Type_Simple)

The first step of the request processing for this product type is validation of custom options. They are get validated by _prepareOptions() method of type model. When options are validated and processed, then it throws an event. The event name depends on type of product processing: catalog_product_type_prepare_full_options or catalog_product_type_prepare_lite_options. Afterwards the product is checked for being added to the cart by grouped parent, in this case additional product options are added. Then _processProduct()method just returns array with a single configured instance of product and special properties set to it (qty, custom options, buy request, etc.).

Grouped Products (Mage_Catalog_Model_Product_Type_Grouped)

Grouped type process is absolutely different from simple product type, however it involves simple products process for child products. Everything starts from retrieving all assigned child products and comparing them to passed child ids in request. If no child products found in request, _prepareProduct() returns string with error message. Otherwise it just invokes _prepareProduct() method of child product type model and adds it to array of configured product instances. When all child products are processed and there is no errors returned, it adds own product instance to the array and returns it.

Configurable Product (Mage_Catalog_Model_Product_Type_Configurable)

The initial process is the same as in simple product and afterwards it checks configured options by finding a correct simple product that matches all the selected values and invokes its _prepareProduct() method. Also if configurable product has custom options, this options partially added to simple product for making it unique in the cart. If no simple product that matches selection is found, it returns string with error text, otherwise array with two items: configurable product itself and its matched child simple product.

Bundle Product (Mage_Bundle_Model_Product_Type)

The initial process as well the same as in simple product. Then all the possible options and their selections gets loaded and each options gets validated. If option is required and at least one selection was not specified in the request, then it returns an error string. Then the selections that were selected are checked for their purchase availability (i.e. isSalable() method is invoked). Later on all the selections are iterated to set selection qty from input or predefined value (if input is empty or customer cannot change it). As well prepareForCart() method gets invoked for each selection product to add its own options. In the end, bundle product model returns bundle product and all its selection products in the array of result.

Other Product Types

There are also some other product types like Virtual and Downloadable, but their process is the same as in Simple Type Model, just with some additional processes of own options.

5. Quote Model Adds a New Item or Changes Existing One Qty

First of all, before creating a new quote item, quote model checks for existence of the product with the same combination of options and product id. For this purpose method getItemByProduct() is used. This method walks though all quote items and calls representProduct() method of quote item model. If method returns true, quote model just updates existing item qty by calling addQty() method. Otherwise creates a new quote item and does the same for it. After qty was changed, quote item dispatches sales_quote_item_qty_set_after event, that is observer by stock module. More about this flow described in Update Items in Cart process. When item qty was set, quote model checks for possible errors in added/modified item. If error were found it throws an exception, otherwise it throws sales_quote_product_add_afterevent.

Grouping of Quote

I just mentioned representProduct() method, that is very interesting one and it is quite good to know, how it works. Because sometimes Magento developer needs to have some custom grouping of the items in cart. Most of developers just rewrite quote item model and override this method. However, there is a small trick you can use. Observe catalog_product_type_prepare_full_options for adding new custom option to your product. And observer code may look like the following:

public function makeMyProductUnique($observer)
    if ($observer->getEvent()->getBuyRequest()->getSomeFlagFromRequest()) {
        $product = $observer->getEvent()->getProduct();
        $product->addCustomOption('my_custom_unique_id', 'some_value');

6. Cart Model Dispatches Add To Cart Event or Throws Exception

If everything was fine during adding product to cart, cart model dispatches an event called checkout_cart_product_add_after and sets property into checkout session object with the latest added product id. This property name is last_added_product_id.  If there were some error during product add to cart process, cart model throws an exception with error message. If error was returned as string from quote addProduct()method, then it is treated as notice. If it was a caught exception, than it will be treaded as error message. Then later on, in controller layer, the exception thrown by cart model is caught and customer redirected to product page with error or notice shown on top of the page.

7. Controller Saves Cart Model

As soon as product is successfully added to cart, controller adds related products via cart model method addProductByIds(). The process of adding related products are almost the same, as all actions described above, except that only simple products without custom options can be added as related by this method. Then save() method of cart model gets invoked.

8. Cart Saves Quote and Collects Totals

Inside of  cart model save() method, goes total calculation process, that is invoked by  collectTotals() method of quote model. More about totals calculation, you can read in the next sections of this article. After totals are collected, save()method of quote model gets invoked. During this operation quote models saves all child entities like address, item, payment info, etc.

9. Controller Redirects Customer to Shopping Cart or Product Page

When item was successfully added to the shopping bag, there is also thrown an event on controller level, it is called checkout_cart_add_product_complete. You can use this event for custom redirect or building own ajax response for your super-duper ajax based add to cart functionality. It is just a small advice for all that extension makers, who would rather override method, instead of observing an event. But if you haven’t observed previous event and haven’t set the property called no_card_redirect to checkout session object, Magento uses own logic for detecting redirect url and adds a success message. Depends on configuration settings, there are two options for redirecting customer back. If config option checkout/cart/redirect_to_cart is set to true, then customer always will be redirected to the cart. Otherwise to the page from which product was added to cart (that is usually a product page).

Configure Added Item

As you might know, starting from CE 1.5.x it is possible to update configurable, bundle and simple products with custom option from the cart instead of removing old one and inserting new one. Actually Magento does almost the same for you, it removes old quote item if there is a change in options and adds a new one. You can see the difference on the following flowchart. Just click on it to view the flowchart in full size. The following flowchart do not represent the process of rendering of configure page, since it is very simple – the same as regular product page, just with set up of selected options. Also this section won’t be so massive like previous one, it describes only new steps and difference in similar actions.

1. Controller Finds Quote Item

Everything starts, when data gets submitted to  checkout/cart/updateItemOptions action. The first thing that controller does, it checks existence of quote item with supplied id in the current quote object. If item doesn’t exists, it adds an error message and redirects customer to shopping cart. If quote item was found, the process flow is delegated to updateItem() method of cart model.

2. Cart Model Prepares Product Request

This process is the same as during adding product to cart, except that method name of quote is called updateItem(). 

3. Quote Model Processes Product Request

 For processing of product request in updateItem() method of quote model, called another method addProduct(). Calling of this method invokes the same process of validation of request and preparing new quote item as during add to cart process.

4. Product Type Model Validation and Processing of Request

The same as during add to cart process.

5. Quote Model Adds a New Item or Changes Existing One Qty

The same as during add to cart process.

6. Quote Model Checks For a New Quote Item Created and Removes Old Quote Item

When addProduct() method returns configured quote item, inside of the updateItem() method, performed a check for item id change. E.g. if any option was changed, addProduct() method will return a new item, otherwise it just updates qty.
If quote item id is not the same as configured item, old one gets removed by calling of removeItem() method of quote model.

7. Cart Model Dispatches Update Item Options Event or Throws Exception

If there were no errors and item was updated, there is dispatched an event with such a name: checkout_cart_product_update_after. Also cart model sets the same property with latest added product in session, even if it just was updated. The processing of the errors is totally the same as in add to cart flow.

8. Controller Saves Cart Model, Cart Saves Quote and Collects Totals

Absolutely the same as in add to cart process.

9. Controller Redirects Customer to Shopping Cart

This step is very similar as in add to cart, but the name of magic event, that you can use of updating response is different. The event name is checkout_cart_update_item_complete. So it is another nice event for extension developers, who’d like to implement ajax based item update.

Update Items In The Cart

From the name of the process it looks like, that it is also related to updating of item options. But it just changes quantity of the item in the cart. Check the following flow chart and of course descriptions of actions below it.

1. Controller Normalizes Localized Qty Values

Everything starts at checkout/cart/updatePost controller action. There are two action flows, but we are not looking at empty_cart one, since it is very simple. It just removes all items from cart and not so interesting like update_qty.  For update_qty action, there is invoked a protected method _updateShoppingCart(), that contains all the logic of updating quote items. First of all it retrieves an associative array of updated items and normalizes qty values according to current locale settings, because Magento supports decimal qty values and not all the countries have dot as decimal mark. So it is required to transform customer input to a correct php decimal representation in string. When qty  values are normalized, controller invokes special suggest qty functionality. It is realized by suggestItemsQty()method in the cart model.

2. Cart Model Updates Qty Array With Suggested Values For Each Quote Item

Inside of suggestItemsQty() method cart model walks through all items in the array. If item if the specified id exists and product is set to a quote item and product has quote item assigned, then it method passes qty to a stock item model method suggestQty(). Result returned from the method call gets set to item in array, so it will be used for updating real qty.

3. Stock Item Checks Qty Increments Value

When cart model calls suggestQty() method, stock item model performs a check of qty increments options. If qty increments option is specified for a stock item, it checks correctness of value specified for a stock item. It also checks if the value is between min and max values, but it doesn’t autocorrect it, it just leaves qty as is, to validate it later on by another process. Here are some test cases, to make it easier for you to get this functionality implementation right: If product has qty increments value set to 3, and min sale qty is 3 and max sale qty is 12 then:

  • If specified qty equals to 2, then it won’t be autocorrected
  • If specified qty equals to 3, then it won’t be autocorrected
  • If specified qty equals to 4, then it will be autocorrected to 3
  • If specified qty equals to 5, then it will be autocorrected to 6
  • If specified qty equals to 6, then it won’t be autocorrected.
  • If specified qty equals to 13, then it won’t be autocorrected

4. Controller Passes Qty Array to Cart Model

When suggested qty functionality process was finished, controller takes modified array and invokes updateItems() method of the cart model.

5. Cart Model Modify Qty Process

When cart model get array of quote items quantities to update as argument to updateItems(), first of all it dispatches checkout_cart_update_items_before event. It is pity, but you cannot modify array of qty’s by observing this event, you can only retrieve it. After dispatching of the event, cart model walks through all array rows. If row qty equals or less than 0, quote item associated to the array row gets removed from cart, otherwise it just sets quote item qty. If any item has difference between qty specified by customer and qty that was actually applied (qty increments modifications), then cart model adds notice message to session object, so customer will get notified about autocorrected values. When all items were modified or deleted, there is dispatched another event, that is called checkout_cart_update_items_after.

6. Quote Item Dispatches Qty Change Event

After setting of qty by cart model, quote item model dispatches sales_quote_item_qty_set_after event. This event can be observed by you to perform additional check of inventory. BTW, this event is observed by Mage_CatalogInventoy module and on this event Magento performs check of item stock availability.

7. Stock  Qty Change Logic

Observer Mage_CatalogInventory_Model_Observer class has specific logic qty change validation of quote item. This logic is realized in checkQuoteItemQty()method. I will not go too much in deep into check activities, just will explain it in general. If quote item qty lower than minimal qty for product or greater than maximum one, it will return an error. If specified qty is not available for item, it also will rise an error.

8. Controller Saves Cart Model, Cart Saves Quote and Collects Totals

Absolutely the same as in add to cart process.

9. Controller Redirects Customer to Shopping Cart

There is no event for updated items qty, so you cannot modify the request via specific cart controller event, but it is still possible to make a trick by adding observer to controller post dispatch event, that is called controller_action_postdispatch_checkout_cart_updatePost. This event can be used for changing redirect to ajax response, but, of course, it adds a lot of work for you to detect possible errors and updated items from session and quote objects.

Remove Item From The Cart

Quite simple process, but also have some interesting features that you should know. Just take a look at the flowchart.

Even if remove process sounds quite simple, however, there are some special features that might be new for you. Especially it is related to logic with parent-child relations. So lets take a look at every process more in detail.

1. Controller Passes Quote Item Id to Cart Model

It starts at checkout/cart/delete action. Controller just retrieves id url parameter and invokes cart model method removeItem().

2. Cart Model Removes Item from Quote

This step is really simple and straightforward. Cart model just calls removeItem() method of quote model.

3. Quote Item Removal Logic

When removeItem() method gets invoked, there is a check for item existence. However, if it doesn’t exists, it won’t rise an error, it just skips item removal logic.

If item exists, it marks it as deleted by invoking isDeleted() method with true value as an argument, so when save() method is invoked during quote save process, item record will be removed from the database. After quote item is marked for delete, there is a check for child quote items assigned to it. If so, it iterates over it and marks child items as deleted as well. Also if that is removed is a child item of some parent one, it will mark parent item as well. So if it has child items or is a child item itself, Magento will remove child/parent item as well.

After item was marked as removed, it dispatches an event that is called sales_quote_remove_item. You can observe it, if you have some special items, that cannot be removed from the cart. Just mark quote item in observer as un-deleted by calling isDeleted() method with first argument value that equals to false.

4. Controller Saves Cart Model, Cart Saves Quote and Collects Totals

Absolutely the same as in add to cart process.

5. Controller Redirects Customer to Shopping Cart

There is also no special event, like in update quote items qty process. Customer just gets redirected back to shopping cart.

View Shopping Cart Page

So, have you ever thought what is happing when you are viewing Shopping Cart Page? If no, then it will be a surprise for you, how many checks and processes are performed, when you land to checkout/cart/index page. Curious? Check the following flowchart. 

1. Controller Checks Number of Quote Items

When you are viewing the shopping cart page, controller checks number of items in your shopping cart. If you have at least one item, it starts initialization of shopping cart model, otherwise just renders shopping cart page (empty cart template). Shopping cart model is initialized by its init() method invokation.

2. Cart Model Resets Checkout Data

When cart model gets initialized, first of all it resets checkout method for Onepage checkout flow. It is done by setting up property checkout_method to empty value for a quote model. Also if there were any steps performed for multiple shipping checkout, e.g. checkout_state property of checkout session object is not equal to Mage_Checkout_Model_Session::CHECKOUT_STATE_BEGIN constant value. In this case all the addresses added to a quote will be removed, also cart model removes payment info instance, since it may affect another checkout type.

As well, if apparently during initialization process all the items were removed from the quote, cart model will remove all the shipping rates.

3. Controller Saves Cart Model, Cart Saves Quote and Collects Totals

Absolutely the same as in add to cart process.

4. Quote Model Validates Minimum Amount

When all the totals are recalculated, controller calls quote model method, that is called validateMinimumAmount(). In this method quote model walks though all addresses and invokes method with the same name on them. If any of those methods return false, quote method will return false as well.

On address model level validateMinimumAmount() method compares configuration value from sales/minimum_order/amount path with amount from address base_subtotal_with_discount property. This property contains subtotal with all discounts.

If minimum amount is not valid, controller adds notice message to checkout session with minimum required amount for purchase.

5. Controller Adds Quote Messages to Checkout Session

When all the initialization and validation are performed, there might by some messages stored in quote model. So controller just retrieves that messages and adds it to checkout session object.

6. Controller Sets Cart Updated Flag

You might be curious, why controller sets updated flag, if nothing was updated? There reason is quite simple, as soon as customer came back to shopping cart page from any checkout page and if in another browser tab he has a checkout page, the data on that checkout page gets invalidated, so customer needs to start over the checkout process. So for this purpose this property changed, since with every ajax or regular request every checkout flow checks it and if it equals true, redirects customer to cart.

7. Controllers Renders Shopping Cart Page

There is no hidden actions at this step, it is quite straight forward. Controller just loads layout and renders it.

What’s Next?

So when you know how main processes work in shopping cart, it is time to dig more into advanced part like totals and addresses. So don’t forget to check our blog in few weeks! Next time you will get aquatinted with Quote Address, Totals and Price Calculation! Also I am going to reveal some secrets of tax applying process.

Ivan Chepurnyi
Magento guru / System architect
Ivan started as Magento Core developer in early 2007, since that time he already has 6 years of experience in different areas of Magento development. During all that time he developed enormous amount of modules and customizations, so now almost every dark corner in Magento functionality is investigated by him. He can’t keep that knowledge in secret, that why he's sharing it with the community and helps finding the way out of Magento complexity maze.

20 Responses to “Magento Checkout: Shopping Cart Flow”

  1. Cool and very useful. Waiting for payment and order processes revealing.

  2. Very useful information about checkout shopping cart flow. Keep posting.

  3. Thanks Ivan, great overview. You had a lot of time the last weeks? :D

  4. Milen Petrov says:

    Great series, cannot wait for the next articles in it. With some small code snippets examples from the core it is becoming a good Magento Checkout Internals eBook :)
    Thanks a lot and waiting for your next post in the series.

  5. i have added custom url parameter to add product to cart

    when i pass following url i want product qty not be added .

    But qty is added only if productid and otherid already exits


  6. hii

    Please anybody tell me how we add product to checkout when products are out of stock

    Thank You

  7. Awesome very rare article to be found. I would suggest you to breakdown the headings and sub-heading with increment prefix or something like that, so that it’s more readable.

  8. This is a really great article – read it a few times now and still trying to wrap my head around this. I am trying to control the way nominal products (recurring profiles) are added to the cart since only one can be checked out at once. I’d like to create an observer that clears the cart when adding a nominal item so that you are never adding more than one at a time and it never conflicts with other products. Any ideas?


  9. Hi Great tutorial. i have one doubt in product view cart page. can you please clarify me. my question i have a multi option in single product how can i get product option parameter in cart page. please advise

  10. XingXing says:

    Great Article!!!

    I have a issue about checkout page on my site.

    I hope you help me about it.

    After I add a product to cart and click checkout button on the cart page, it directs Shopping Cart Empty page.

    Strange is, I could see checkout page on my FireFox sometimes, but I couldn’t see checkout page on my other browser and other computers.

    You can check this issue yourself here.

    I hope you advise about this issue.

    Thank you.

    Kind Regards, XingXing

  11. Milen Petrov says:

    When we should expect the next part of this great series of articles?
    Thanks in advance

  12. Rohan Kulkarni says:

    Very nice and unique article regarding checkout process i haven’t seen such well gathered information of checkout process anywhere

    keep up magento guru we expect more articles from u


  13. I get all details of magento adding to cart workflow with this great article and Magento source code. You guys really did a great job! Thank you very much!!

  14. This is a fantastic walkthrough of the cart workflow. I’m trying to use your information to solve an issue we have where the qty field in the cart is multiplying the entered figure by 10 (but without changing the displayed figure) which is causing most checkouts to return an error saying the product is not available in the requested quantity.

    You can see full details of this issue at Stackoverflow.

    It would be greatly appreciated if you could take a look Ivan and see if you had any ideas at all. Thanks.

  15. Its really great post, Thank you very much.

  16. Very very usefull post! Thanks a lot. I’m hurry to read your next post. Will you write about collect totals process? Oh please :)

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>