I am using the observer checkout_cart_save_after to programatically delete some cart items.

public function execute(Observer $observer)
    $cart = $observer->getCart();
    $quote = $cart->getQuote();
    $cartItems = $quote->getAllItems();

    foreach ($quote->getAllItems() as $item) {

Whilst this does delete the cart items, it still shows the checkout page as an empty table like so:

Programatically deleted cart

But if I delete all the items manually on the frontend, I get a much nicer page after I delete the last item:

Manually deleted cart

What am I missing?


2 Answers 2


Short Answer

Use the checkout_cart_save_before event, this way it will serve the correct cart page as well as handling calculating the totals correctly.

Breakdown on why

The logic that decides what page you get served is in the following method Magento\Checkout\Block\Cart->getItemsCount() which in turn calls getItemsCount on the quote.

If the method returns a truthy value, you will get served the cart with the sidebar otherwise you get the one without.

The items_count data property on the quote is only, with the exception of the dot mailer module, set when you collect totals for the quote in Magento\Quote\Model\Quote\TotalsCollector->_collectItemsQtys() which is fired from the collect method within the same class.

The order of execution for saving the cart is as follows: (removed unrelated bits for simplicity)

  • Dispatch Event checkout_cart_save_before
  • Set Billing/Shipping Address
  • Collect Totals (Here items_count is set)
  • Dispatch Event checkout_cart_save_after

So as you can see by using the checkout_cart_save_after event, your code is fired after the totals are collected. Meaning the cart totals/items count is still set as the values before you removed the items.

And I believe (don't quote me on this) where the totals on the cart are loaded with ajax, this recalls the method to collect the totals. Which then causes the ajax'd totals to pull the correct items_count and totals through. Although at this point its too late since the cart is already rendered.


Ideally, if you want to modify the items in the quote you would use the before event checkout_cart_save_before so when it calculates the shipping rates/totals if takes into account your changes.

Or you could recollect the totals after you've made your changes in the after event. $quote->setTotalsCollectedFlag(false)->collectTotals()

Or you can manually update the items_count value on the quote after you remove an item from it like you are currently doing.


Seems that for whatever reason, the item count for the quote object never hit 0. I ended up having to write some logic to update it at the end of the observe...

$quote->setItemsCount($quote->getItemsCount() - 1);
$quote->setItemsQty($quote->getItemsQty() - 1);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.