In this article we will examine in great detail the question from Magento 2 Certification – Customizing the Checkout Process. The topic itself is rather complicated, so we got here a very large post, so for you to navigate in it, here’s the three aspects we’re going investigate:
How to use quote, quote item, address, and shopping cart rules in checkout
How to use totals models
How to customize the shopping cart
How to use quote, quote item, address, and shopping cart rules in checkout
What is quote?
Let’s begin with the notion of a quote itself, what is it used for and where is it stored. When a customer adds a new item to the cart, Magento creates a Quote. It is used for the following purposes:
- to track the products that a customer wants to buy along with their cost and quantity;
- to collect customer information, including billing address and shipping address;
- to assess Shipping Cost;
- to calculate the subtotal price, to add (shipping cost, taxes, etc.) and use coupons for determining the total amount;
- to determine the payment method;
- to place an order so that the seller can fulfill it.
There are the following tables for storing Quote in the database:
- quote
- quote_address
- quote_address_item
- quote_id_mask
- quote_item
- quote_item_option
- quote_payment
- quote_shipping_rate
The Magento\Quote\Model\Quote model is responsible for Quote. The Magento\Quote\Model\Quote\Address\Quote model is responsible for Quote Address and usually contains 2 addresses (billing, shipping), but can have more (if the customer specified multiple shipping addresses) or none at all. If there is no address in Quote, then the Price Rules associated with the country will not be taken into account in Totals. If Quote contains virtual products only, then the shipping address is ignored and checked by the isVirtual() method of the Magento\Quote\Model\Quote class. Only the billing address is considered for Quote with virtual products. The Magento\Quote\Model\Quote\Item model is responsible for products in Quote. The Magento\Quote\Model\Quote\Payment model is responsible for payment in Quote.
Useful quote fields or methods
The “quote” table has a field called “ext_shipping_info” where you can add some extra information.
If “quote” has the “trigger_recollect” flag and its elements were changed, they will be reassembled via the Magento\NegotiableQuote\Plugin\Quote\Model\QuotePlugin around CollectTotals() function (Magento Commerce only). After performing the collectTotals() function, the elements marked as deleted will be deleted. This method compares the elements that existed in “quote” before collectTotals() was called. If certain elements are absent after the function has been executed, this method sets the deletion notifications of them.
If the “trigger_recollect” flag is set, then provided the price is changed or the product status is set to “disabled”, quote will be updated after the item is saved. The same happens if we change the rules of the “CatalogRules” directory. We can find these methods in the Magento\Quote\Model\ResourceModel markQuotesRecollectOnCatalogRules() and markQuotesRecollect() classes.
Quote can also be extended via Magento\Framework\Model\AbstractExtensibleModel — it supports “extension_attributes“. You can register the extension via the extension_attributes.xml file:
1 2 3 4 |
<extension_attributes for="Magento\Quote\Api\Data\CartInterface"> <attribute code="shipping_assignments" type="Magento\Quote\Api\Data\ShippingAssignmentInterface[]" /> </extension_attributes> |
Use the “Quote Repository Plugin” to fill in your values using the afterLoad(), beforeSave() or whenever() functions. Quote does not use “custom_attributes” as they are not EAV.
Quote address custom attributes
- “CE” \Magento\Quote\Model\Quote\Address\CustomAttributeList getAttributes() returns empty array. A plugin needs to be written for the implementation.
- “EE” \Magento\CustomerCustomAttributes\Model\Quote\Address\CustomAttributeList::getAttributes returns “customer address attributes” + “customer attributes”.
Quote item useful methods:
- Magento\Quote\Model\Quote\Item checkData() is called after adding to cart and updating options;
- Magento\Quote\Model\Quote\Item setQty() triggers stock validation;
- Magento\Catalog\Model\Product\Type\AbstractType checkProductBuyState() checks if product can be bought;
- Magento\Quote\Model\Quote\Item setCustomPrice();
- Magento\Quote\Model\Quote\Item getCalculationPrice() gets original custom price applied before tax calculation;
- Magento\Quote\Model\Quote\Item isChildrenCalculated() checks if there children calculated or parent item when we have parent quote item and its children;
- Magento\Quote\Model\Quote\Item isShipSeparately() checks if we can ship the product separately (each child separately) or each parent product item can be shipped only as a single item;
- Magento\Quote\Model\Quote\Item\Compare::compare merges items and adds qty instead of a new item;
- Magento\Quote\Model\Quote\Item representProduct() compares the quote item with some new product, checks product id and custom options;
- Magento\Quote\Model\Quote\Item compareOptions() check if two option arrays are identical. The first option array is prerogative, the second options array is checked against the first one.
How to modify these models and effectively use them in customizations
Inventory validation
- Use function Magento\Quote\Model\Quote\Item setQty() to declare quote item quantity.
- Use event “sales_quote_item_qty_set_after“.
- Use function Magento\CatalogInventory\Observer\QuantityValidatorObserver::execute.
- Use function \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator::validate to check product inventory data when declaring quote item quantity.
Add to cart
- Use function Magento\Checkout\Model\Cart addProduct() to add a product to the shopping cart (quote).
- Use function Magento\Quote\Model\Quote addProduct() for advanced functionality of adding a product to quote (you can specify the processing mode there). It returns the error message if the product type instance can’t prepare the product.
- Use function Magento\Catalog\Model\Product\Type\AbstractType prepareForCartAdvanced() to initialize product(s) for the ‘add to cart’ process. The advanced version of the function that prepares a product for cart (processMode) can be specified there.
- Use function Magento\Quote\Model\Quote\Item\Processor::prepare to set qty and custom price for quote item.
- Use event “checkout_cart_product_add_after“.
- Use event “checkout_cart_save_after”, “checkout_cart_save_before“.
- Use event “checkout_cart_add_product_complete“.
Cart update
- Use function Magento\Checkout\Model\Cart suggestItemsQty() to return the suggested quantities for items. It can be used to automatically fix the quantities entered by the user before updating cart so that cart contains valid qty values.
- Use function Magento\Checkout\Model\Cart updateItems() to update the information about cart items.
- Use function Magento\Checkout\Model\Cart save() to save cart.
How to customize the process of adding a product to the cart
- Use function Magento\Catalog\Model\Product\Type\AbstractType prepareForCartAdvanced() to prepare the product for adding to cart (i.e the “Quote” object). It is called from the Magento\Quote\Model\Quote addProduct() method.
- The “catalog_product_type_prepare_full_options” event is launched right before the product is converted into the “Quote” element with its configured options.
- Using the \Magento\Quote\Model\Quote\Item\Processor::prepare plugin we can change the product quantity and its custom price.
Using the “checkout_cart_product_add_after” event we can change attribute values or product price. - In the catalog_attributes.xml file of the <group name=”quote_item”> group we can describe the product attributes that will fall into “quote”. For example, <attribute name=”sku”/>.
Different scenarios you should take into account
- adding to the cart from the catalog;
- adding to the cart from wishlist;
- adding all the items from wishlist to the cart;
- placing the order from the administrative / user part of the site;
- “Reorder” from the administrative / user part of the site;
- configuration of the added product — changing custom options;
- combining “Quotes” when authorizing a client if he added products as a guest and already has a “Quote”.
Magento Webdesign
Take your online store to the next level with BelVG Magento Webdesign
Visit the pageHow to use totals models
How can you add a custom totals model or modify existing totals models?
First of all you need to create in our module the following file app/code/Vendor/Module/etc/sales.xml. This file is used to register all available ones in Magento totals.
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd"> <section name="quote"> <group name="totals"> <item name="custom_total" instance="Vendor\Module\Model\Totals\Custom" sort_order="500"/> </group> </section> </config> |
Here we specified the \Vendor\Module\Model\Totals\Custom class as a model of our custom total. This class must inherit \Magento\Quote\Model\Quote\Address\Total\AbstractTotal and realize collect и fetch methods. The collect method is used to calculate our total and fetch method recovers this sum as well as total’s code and its name.
Moreover, the parameter \Magento\Quote\Model\Quote\Address\Total $total allows you to affect the result of the other total classes. But depending on the task it can make more sense to use plugins to modify its values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
<?php Namespace Vendor\Module\Model\Total; class Custom extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { /** * Custom constructor. */ public function __construct() { $this->setCode('custom_total'); } /** * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this */ public function collect( \Magento\Quote\Model\Quote $quote, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, \Magento\Quote\Model\Quote\Address\Total $total ) { parent::collect($quote, $shippingAssignment, $total); $items = $shippingAssignment->getItems(); if (!count($items)) { return $this; } //we will add an additional amount of 150 to the order as an example $amount = 150; $total->setTotalAmount('custom_total', $amount); $total->setBaseTotalAmount('custom_total', $amount); $total->setCustomAmount($amount); $total->setBaseCustomAmount($amount); $total->setGrandTotal($total->getGrandTotal() + $amount); $total->setBaseGrandTotal($total->getBaseGrandTotal() + $amount); return $this; } /** * @param \Magento\Quote\Model\Quote\Address\Total $total */ protected function clearValues(\Magento\Quote\Model\Quote\Address\Total $total) { $total->setTotalAmount('subtotal', 0); $total->setBaseTotalAmount('subtotal', 0); $total->setTotalAmount('tax', 0); $total->setBaseTotalAmount('tax', 0); $total->setTotalAmount('discount_tax_compensation', 0); $total->setBaseTotalAmount('discount_tax_compensation', 0); $total->setTotalAmount('shipping_discount_tax_compensation', 0); $total->setBaseTotalAmount('shipping_discount_tax_compensation', 0); $total->setSubtotalInclTax(0); $total->setBaseSubtotalInclTax(0); } /** * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Model\Quote\Address\Total $total * @return array */ public function fetch( \Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total ) { return [ 'code' => $this->getCode(), 'title' => 'Custom Total’, 'value' => 150 ]; } /** * @return \Magento\Framework\Phrase */ public function getLabel() { return __('Custom Total’); } } |
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.How to use totals models
Cart and checkout pages
To demonstrate totals we use knockout.js, so in order for our total to appear at the cart and checkout pages, we need to add new js component to checkout_cart_index.xml and checkout_index_index.xml layouts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.cart.totals"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="components" xsi:type="array"> <item name="block-totals" xsi:type="array"> <item name="children" xsi:type="array"> <item name="custom_total" xsi:type="array"> <item name="component" xsi:type="string">Vendor_Module/js/view/checkout/cart/totals/custom_total</item> <item name="sortOrder" xsi:type="string">20</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Vendor_Module/checkout/cart/totals/custom_total</item> <item name="title" xsi:type="string">Custom Total</item> </item> </item> </item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page> |
app/code/Vendor/Module/view/frontend/layout/checkout_index_index.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="components" xsi:type="array"> <item name="checkout" xsi:type="array"> <item name="children" xsi:type="array"> <item name="sidebar" xsi:type="array"> <item name="children" xsi:type="array"> <item name="summary" xsi:type="array"> <item name="children" xsi:type="array"> <item name="totals" xsi:type="array"> <item name="children" xsi:type="array"> <item name="custom_total" xsi:type="array"> <item name="component" xsi:type="string">Vendor_Module/js/view/checkout/cart/totals/custom_total</item> <item name="sortOrder" xsi:type="string">20</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">Vendor_Module/checkout/cart/totals/custom_total</item> <item name="title" xsi:type="string">Custom Total</item> </item> </item> </item> </item> <item name="cart_items" xsi:type="array"> <item name="children" xsi:type="array"> <item name="details" xsi:type="array"> <item name="children" xsi:type="array"> <item name="subtotal" xsi:type="array"> <item name="component" xsi:type="string">Magento_Tax/js/view/checkout/summary/item/details/subtotal</item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page> |
We also need to add the components themselves and HTML templates: app/code/Vendor/Module/view/frontend/web/js/view/checkout/cart/totals/custom_total.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
define( [ 'Vendor_Module/js/view/checkout/summary/custom_total' ], function (Component) { 'use strict'; return Component.extend({ /** * @override */ isDisplayed: function () { return this.getPureValue() !== 0; } }); } ); |
app/code/Vendor/Module/view/frontend/web/template/checkout/cart/totals/custom_total.html
1 2 3 4 5 6 7 8 9 |
<!-- ko if: isDisplayed() --> <tr class="totals custom_total excl"> <th class="mark" colspan="1" scope="row" data-bind="text: title"></th> <td class="amount"> <span class="price" data-bind="text: getValue()"></span> </td> </tr> <!-- /ko → |
app/code/Vendor/Module/view/frontend/web/js/view/checkout/summary/custom_total.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
define( [ 'Magento_Checkout/js/view/summary/abstract-total', 'Magento_Checkout/js/model/quote', 'Magento_Catalog/js/price-utils', 'Magento_Checkout/js/model/totals' ], function (Component, quote, priceUtils, totals) { "use strict"; return Component.extend({ defaults: { isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, template: 'Vendor_Module/checkout/summary/custom_total' }, totals: quote.getTotals(), isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, isDisplayed: function() { return this.isFullMode() && this.getPureValue() !== 0; }, getValue: function() { var price = 0; if (this.totals()) { price = totals.getSegment('custom_total').value; } return this.getFormattedPrice(price); }, getPureValue: function() { var price = 0; if (this.totals()) { price = totals.getSegment('custom_total').value; } return price; } }); } ); |
getValue and getPureValue methods retrieve the value of our custom total, but the getValue method formats this value adding two decimal digits and the preset currency symbol to it.
app/code/Vendor_Module/view/frontend/web/template/checkout/summary/custom_total.html
1 2 3 4 5 6 7 8 9 10 11 |
<!-- ko if: isDisplayed() --> <tr class="totals custom_total excl"> <th class="mark" scope="row"> <span class="label" data-bind="text: title"></span> <span class="value" data-bind="text: getValue()"></span> </th> <td class="amount"> <span class="price" data-bind="text: getValue(), attr: {'data-th': title}"></span> </td> </tr> <!-- /ko --> |
Order Emails
In order to add the reflection of a new total to the order email, you need to add a new block to the sales_email_order_items layout.
app/code/Vendor/Module/view/frontend/layout/sales_email_order_items.xml
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="UTF-8"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="order_totals"> <block class="Vendor\Module\Block\Order\CustomTotal" name="order.totals.custom" /> </referenceBlock> </body> </page> |
app/code/Vendor/Module/Block/Order/CustomTotal.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace Vendor\Module\Block\Order; class CustomTotal extends \Magento\Framework\View\Element\AbstractBlock { public function initTotals() { $orderTotalsBlock = $this->getParentBlock(); $order = $orderTotalsBlock->getOrder(); if ($order->getCustomAmount() > 0) { $orderTotalsBlock->addTotal(new \Magento\Framework\DataObject([ 'code' => 'custom_total', 'label' => __('Custom Total'), 'value' => $order->getCustomAmount(), 'base_value' => $order->getCustomBaseAmount(), ]), 'subtotal'); } } } |
Magento Custom Development
Take your online store to the next level with BelVG Magento Custom Development
Visit the pageHow to customize the shopping cart
How to create shopping cart rules in Magento 2
Cart rules in Magento 2 is a system used for discounts and promotion. Unlike catalog rules, cart rules are applied not to a certain product in a shopping cart, but to the whole cart itself; another difference is that a customer might need to enter a coupon code to trigger a shopping cart rule. Such discounts that are not visible to the customer and are activated if there are certain factors for obtaining them, like the number of products in the customer’s basket, their total price, product category, and many others.
Create cart rules from admin panel
Such rules are created mainly from the admin panel, but you can also create it programmatically. To create a new cart rule from the admin panel, go to Marketing -> Cart Price Rules and click “Add New Rule”.
There you can choose the name of the rule, its correlation to store and customer groups, and set the coupon necessary for getting the discount.
Create new cart rule programmatically
To create shipping cart rule programmatically, you have to inject \Magento\SalesRule\Model\RuleFactory class:
1 2 3 4 5 6 7 8 9 10 |
<?php .... protected $ruleFactory public function __construct( \Magento\SalesRule\Model\RuleFactory $ruleFactory ) { $this->rulesFactory = $ruleFactory } |
And then use it like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php … $ruleData = [ "name" => "Custom Cart Rule", "description" => "Buy some products and get one more free", "from_date" => null, "to_date" => null, "uses_per_customer" => "0", "is_active" => "1", "stop_rules_processing" => "0", "is_advanced" => "1", "product_ids" => null, "sort_order" => "0", "simple_action" => "buy_x_get_y", "discount_amount" => "1.0000", "discount_qty" = >null, "discount_step" => "3", "apply_to_shipping" => "0", "times_used" => "0", "is_rss" => "1", "coupon_type" => "NO_COUPON", "use_auto_generation" => "0", "uses_per_coupon" => "0", "simple_free_shipping" => "0", "customer_group_ids" => [0, 1, 2, 3], "website_ids" => [1], "coupon_code" => null, "store_labels" => [], "conditions_serialized" => '', "actions_serialized" => '' ]; $ruleModel = $this->ruleFactory->create(); $ruleModel->setData($ruleData); $ruleModel->save(); |
Create conditions and actions for the rule
As an example, we will create a condition which grants the discount for a certain product if a customer added more than five of them in his cart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
<?php .... protected $ruleFactory protected $productFoundConditionFactory; protected $productConditionFactory; public function __construct( \Magento\SalesRule\Model\RuleFactory $ruleFactory, \Magento\SalesRule\Model\Rule\Condition\Product\FoundFactory $productFoundConditionFactory, \Magento\SalesRule\Model\Rule\Condition\ProductFactory $productConditionFactory, ) { $this->rulesFactory = $ruleFactory; $this->productFoundConditionFactory = $productFoundConditionFactory; $this->productConditionFactory = $productConditionFactory; } … $discount = ‘25’; $sku = ‘PRODUCT_SKU’; $shoppingCartPriceRule = $this->rulesFactory->create(); $shoppingCartPriceRule->setName('25% off with multiple products - ' . $sku) ->setDescription('Get 25% off with two or more products) ->setFromDate('2000-01-01') ->setToDate(NULL) ->setUsesPerCustomer('0') ->setCustomerGroupIds(array('0','1','2','3',)) ->setIsActive('1') ->setStopRulesProcessing('0') ->setIsAdvanced('1') ->setProductIds(NULL) ->setSortOrder('1') ->setSimpleAction(‘by_percent’) ->setDiscountAmount($discount) ->setDiscountQty(NULL) ->setDiscountStep('0') ->setSimpleFreeShipping('0') ->setApplyToShipping('0') ->setTimesUsed('0') ->setIsRss('0') ->setWebsiteIds(array('1',)) ->setCouponType('1') ->setCouponCode(NULL) ->setUsesPerCoupon(NULL); $productFoundCondition = $this->productFoundConditionFactory->create() ->setType('Magento\SalesRule\Model\Rule\Condition\Product\Found') ->setValue(1) // 1 == FOUND ->setAggregator('all'); // match ALL conditions $productCondition = $this->productConditionFactory->create() ->setType('Magento\SalesRule\Model\Rule\Condition\Product') ->setAttribute('sku') ->setOperator('==') ->setValue($sku); $productFoundCondition->addCondition($productCondition); $shoppingCartPriceRule->getConditions()->addCondition($productFoundCondition); $skuCondition = $this->productConditionFactory->create() ->setType('Magento\SalesRule\Model\Rule\Condition\Product') ->setAttribute('sku') ->setOperator('==') ->setValue($sku); $shoppingCartPriceRule->getActions()->addCondition($skuCondition); $qtyCondition = $this->productConditionFactory->create() ->setType('Magento\SalesRule\Model\Rule\Condition\Product') ->setAttribute('quote_item_qty') ->setOperator('>=') ->setValue('2'); $shoppingCartPriceRule->getActions()->addCondition($qtyCondition); $shoppingCartPriceRule->save(); |
Cart rule limitations and performance impact
- One coupon per customer.
- Rules cannot add other products to the cart.
- Rules only apply to the item they were designated to work with.
Cart rules can slow down the process of adding products to the cart as well as work of the shopping cart and checkout. - The more cart rules with a wide range of actions without reference to the website or customer group and the need to use a coupon you will add, the better the performance impact of cart rules will be.
How to add-to-cart logic in different scenarios
- Adding from product pages:
Controllers: \Magento\Checkout\Controller\Cart\Add
Available events: checkout_cart_add_product_complete, checkout_cart_product_add_after, sales_quote_product_add_After, sales_quote_add_item
Based on the added product and the amount needed cart model will generate correspoding quote item. - Adding from wishlist:
Controllers: \Magento\Wishlist\Controller\Index\Cart
Available events: checkout_cart_product_add_after, sales_quote_product_add_after, sales_quote_add_item - Reorder:
Controllers: \Magento\Sales\Controller\AbstractController\Reorder
Available events: checkout_cart_product_add_after, sales_quote_product_add_after, sales_quote_add_item - Quote merge:
Method: \Magento\Quote\Model\Quote::merge
Available events: sales_quote_add_item
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.Difference in behavior of different product types in the shopping cart
Magento 2 implements renderers to show products in the shopping cart. Each product type (e.g. simple, configurable, bundle) has its own renderer. They are registered in a renderer list with the help of layout instructions as child blocks of the \Magento\Framework\View\Element\RendererList block with the name “checkout.cart.item.renderers”.
Let’s take a renderer for configurable products as an example:
vendor/magento/module-configurable-product/view/frontend/layout/checkout_cart_item_renderers.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0"?> <!-- /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.cart.item.renderers"> <block class="Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable" as="configurable" template="Magento_Checkout::cart/item/default.phtml"> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions" name="checkout.cart.item.renderers.configurable.actions" as="actions"> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit" name="checkout.cart.item.renderers.configurable.actions.edit" template="Magento_Checkout::cart/item/renderer/actions/edit.phtml"/> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove" name="checkout.cart.item.renderers.configurable.actions.remove" template="Magento_Checkout::cart/item/renderer/actions/remove.phtml"/> </block> </block> </referenceBlock> </body> </page> |
Where the \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable class is used as a renderer and contains methods for reflecting the child product picture instead of the image of the parent product.
Custom shopping cart renderer
You will need to create block, \Vendor\Module\Block\Cart\CustomRenderer for example, which will be responsible for the html content of the product. Specify the product you need to reflect in the \Magento\Checkout\Block\Cart\AbstractCart::getItemHtml block.
1 2 3 4 5 |
public function getItemHtml(\Magento\Quote\Model\Quote\Item $item) { $renderer = $this->getItemRenderer($item->getProductType())->setItem($item); return $renderer->toHtml(); } |
Therefore, the product we need will be available in the template of our block through the $product = $block->getItem(); call.
Also, you need to register renderer in the list: app/code/Vendor/Module/view/frontend/layout/checkout_cart_item_renderers.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.cart.item.renderers"> <block class="\Vendor\Module\Block\Cart\CustomRenderer" as="custom-type" template="Magento_Checkout::cart/item/default.phtml"> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions" name="checkout.cart.item.renderers.custom.actions" as="actions"> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit" name="checkout.cart.item.renderers.custom.actions.edit" template="Magento_Checkout::cart/item/renderer/actions/edit.phtml"/> <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove" name="checkout.cart.item.renderers.custom.actions.remove" template="Magento_Checkout::cart/item/renderer/actions/remove.phtml"/> </block> </block> </referenceBlock> </body> </page> |
Afterwards, all the “custom-type” product types will be reflected in our class.
Available shopping cart operations
Which operations are available to the customer on the cart page?
- Change product quantity
- Update shopping cart (necessary after changing quantities)
- Change product configurable/bundle options
- Delete product
- Transfer product to the wishlist (in enabled)
- Add/delete coupon
- Proceed to checkout
- Go to multi-shipping settings (if enabled)
How would you create an extension that deletes one item if another was deleted?
You can use an observer for sales_quote_remove_item event. app/code/Vendor/Module/etc/events.xml
1 2 3 4 5 6 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="sales_quote_remove_item"> <observer name="removeCartItem" instance="Vendor\Module\Observer\RemoveCartItem" /> </event> </config> |
app/code/Vendor/Module/Observer/RemoveCartItem.php
1 2 3 4 5 6 7 8 9 10 11 |
namespace Vendor\Module\Observer; class RemoveCartItem implements \Magento\Framework\Event\ObserverInterface { public function execute(\Magento\Framework\Event\Observer $observer) { $deletedItem = $observer->getData('quote_item'); // И тут мы можем проверить id или sku удаленной cart item и в зависимости от этого удалить еще одну. } } |
How do you add a field to the shipping address?
To add a new field to the shipping address, you need to:
1. Add a field to the layout.
Since the shipping and billing address forms are dynamically generated, you need to do this with the help of a plugin for the \ Magento \ Checkout \ Block \ Checkout \ LayoutProcessor :: process method
2. Create a JS mixin to send additional info.
It is necessary to modify the component Magento_Checkout / js / action / set-shipping-information
3. Add new field to the address model with the help of extension attributes.
/app/code/Vendor/Module/etc/extension_attributes.xml
1 2 3 4 5 6 7 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <extension_attributes for="Magento\Quote\Api\Data\AddressInterface"> <attribute code="custom_field" type="string" /> </extension_attributes> </config> |
4. Get server-side value using the Magento \ Checkout \ Api \ Data \ ShippingInformationInterface interface’s getExtensionAttributes method.
Types of payment methods
Payment methods are divided into offline (check / money order, bank transfer) and online (paypal, braintree, authorize.net) methods.
Unlike offline methods, online methods when performing operations such as authorize, capture, refund, etc. send requests to other external payment services.
This is all to the topic of checkout process customization in Magento 2. Feel free to leave your questions and comments down below.
Find a wide range of Magento 2 extensions at out BelVG store.
Magento Webdesign
Take your online store to the next level with BelVG Magento Webdesign
Visit the page
Just mind-blowing writing.
Hi, Umar!
Thanks for your comment and kind words, they are very motivating. Stay tuned for my next posts!
Hi, Umar!
It’s very pleasant to learn you have such high opinion of my article. Stay tuned for my next posts!
That was probably the best article related to checkout on the internet! Good luck for the next posts.