Changes in Commerce 2

Twig template changes

Services available in Twig

The commerce variable class is now gone, and not loaded into the craft.commerce variable anymore. Instead craft.commerce now returns the Commerce plugin instance.

All Commerce service methods can now be accessed through the plugin instance.


craft.commerce.paymentCurrencies will return the PaymentCurrencies service class. craft.commerce.paymentCurrencies.getAllPaymentCurrencies() will call the method on the class to return all payment currency models.

Element queries

The element queries now exist on the root craft variable.


To get all products in Commerce 1.2.x: craft.commerce.products.find()

To do the same in Commerce 2: craft.products.all()


Deprecation of variables was preferred, but breaking changes could not be avoided due to naming collisions between the service names and the previous variable name.

Use the table below to update your twig templates. D - Deprecated BC - Breaking Change


Form Action Changes

commerce/cart/removeLineItemcommerce/cart/update-cartUpdating the Cart
commerce/cart/updateLineItemcommerce/cart/update-cartUpdating the Cart
commerce/cart/removeAllLineItemscommerce/cart/update-cartUpdating the Cart
commerce/cart/updateCartcommerce/cart/update-cartAdding to Cart

Query Changes

Order Queries

Order element queries now returns all orders and carts, regardless of whether they are completed or not.

Previously craft.commerce.orders would only return completed orders, now you need to append .isCompleted(true) to retrieve orders that finished. Otherwise you will get both completed and incomplete orders (carts).

Product Queries

Product element queries previously had a hasSales param to only return product with active sales applied to one or more of its variants. The param has now moved to the variant query, and you can achieve the same thing with the hasVariants param:

You can now do:


{% set products = craft.products.hasVariant({hasSales: true}).all() %}


$products = Product::find()->hasVariant(['hasSales' => true])->all();

Model Changes

Purchasables (like variants).


Updating the cart

In Commerce 2, there has been a change to how the update cart controller action works. All cart actions now return a cart variable from all cart controller actions for both success and failure.

Now, if any part of the update fails, no changes will be saved to the cart, and the returned cart will have errors applied. Previously when updating 2 different things on the cart, one could fail and the other one could succeed. Now the update cart action will only fully succeed or fail. There are no partially applied updates to the cart.

Previously you needed to retrieve the cart with:

{% set cart = craft.commerce.cart %}

This cart would have any errors applied to its attributes, but there was no easy way to access the original cart before the errors. We were also limited to a single flash message with the first error.

Commerce 2 we recommend doing something like this:

{% if cart is not defined %}
  {% set cart = craft.commerce.getCarts().getCart() %} // Gets the clean (no errors) cart from the session/db
{% endif %}

This allows you to use the cart returned from the update cart actions (with its errors applied), or the last known good cart.

The changes mean a faster cart that reduces the number of database updates.

Setting addresses in update cart

Previously in the update cart controller action you would need to submit the string new or a non ID in the place of an address ID, to add an address to the customers address book. The updating of the addresses on the cart has now been simplified with its removal.

To set an existing address on a cart submit the shippingAddressId or billingAddressId param to the update cart action. Submitting these params does not allow any updating of the address contents, it just chooses the address you want to set on the cart. If either of these is submitted, the corresponding shippingAddress and billingAddress post params with any of their contents will not be used to update the cart.


<form method="POST">
 <input type="hidden" name="action" value="commerce/cart/update-cart">
 {{ redirectInput('shop/shipping') }}
 {{ csrfInput() }}
 <input type="hidden" name="shippingAddressId" value="5"/>
 <input type="text" name="shippingAddress[firstName]" value=""/>
 <input type="text" name="shippingAddress[lastName]" value=""/>

In the above example, since we are submitting a shippingAddressId, the shippingAddress data will be ignored.

To submit a new address, or update an existing address while submitting to the cart submit the shippingAddress and/or billingAddress params.

Example 1:

<form method="POST">
 <input type="hidden" name="action" value="commerce/cart/update-cart">
 {{ redirectInput('shop/shipping') }}
 {{ csrfInput() }}
 <input type="text" name="shippingAddress[firstName]" value=""/>
 <input type="text" name="shippingAddress[lastName]" value=""/>

The above would submit a new address to the cart and save it to the customer’s address book.

Example 2:

<form method="POST">
 <input type="hidden" name="action" value="commerce/cart/update-cart">
 {{ redirectInput('shop/shipping') }}
 {{ csrfInput() }}
 <input type="text" name="shippingAddress[id]" value="5"/>
 <input type="text" name="shippingAddress[firstName]" value=""/>
 <input type="text" name="shippingAddress[lastName]" value=""/>

The above would update the shipping address with ID of 5 (that already belongs to the customer) and sets it on the cart.

We have also deprecated the sameAddress param when setting addresses on the cart, and introduced two new params:

billingAddressSameAsShipping and shippingAddressSameAsBilling

Both params are a boolean, and only one can be set per request or a validation error will be thrown on the order. Both work with either the billingAddressId method or the billingAddress data method of updating carts.

Cart Validation

The cart (order) now places all errors on the fields that have the error, and also on the order/cart model with a error key to the location of the error.

For example, if an error is on the second line item, you can access the errors on the line item like this:


this may return an error array like this:

['qty' => 'Maximum quantity allowed is 3']

But in addition to this, the errors will also be on the order


will return an error array like this:

['lineItem[1].qty' => 'Maximum quantity allowed is 3']

Addresses when user session changes

Previously in Commerce 1.x we would strip the addresses from the order when the user logs in. This no longer occurs.

Previously the addresses would also drop if a user logged out, but with the cart remaining in session. In Commerce 2, the whole cart is forgotten when the user logs out.

Example Templates

If you’d like to use Commerce’s sample store templates as a starting point with all of the above changes included, you can copy them from your vendor/craftcms/commerce/templates/shop folder to your templates/shop folder.