The checkout process in Magento 2 it's more practice and more abstracted with new generation ideas of user experience , in this course ibnab try to give you some keys for customize the checkout page .
Saw the system use component for create and manage the page checkout, it's start from the layout checkout_index_index.xml in path vendor/magento/module-checkout/view/frontend/layout , the first line is :
<block class="Magento\Checkout\Block\Onepage" name="checkout.root" template="onepage.phtml" cacheable="false">
the block in path vendor/magento/module-checkout/Block/Onepage.php and template is in path vendor/magento/module-checkout/view/frontend/templates/onepage.phtml
ok if you open onepage.phtml as result you have some lines but the result in browser is complex structure and many element form .
Here we use the technique of js component with knouckoutjs , inside this file you have the big parent div is <div id="checkout" data-bind="scope:'checkout'" class="checkout-container"> we have here 2 important attribute id="checkout" and data-bind="scope:'checkout'" :
id="checkout" : we applied the init of component on it :
<script type="text/x-magento-init"> { "#checkout": { "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout();?> } } </script>
data-bind="scope:'checkout'" : it's used to determinate what's the current scope for example is used inside template .html related to this component , for determinate for where getting the function used by knoukoutjs .
Ok our component initialized in div with id checkout and scope checkout' , we passed as params :
<?php /* @escapeNotVerified */ echo $block->getJsLayout();?> , this php function is inside our block Magento\Checkout\Block\Onepage :
public function getJsLayout() { foreach ($this->layoutProcessors as $processor) { $this->jsLayout = $processor->process($this->jsLayout); } return \Zend_Json::encode($this->jsLayout); }
ohhhhhhh , what's that ? , we have here as class propriety $this->layoutProcessors passed by DI (dependency injection) with constructor :
public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Data\Form\FormKey $formKey, \Magento\Checkout\Model\CompositeConfigProvider $configProvider, ) { parent::__construct($context, $data); $this->formKey = $formKey; $this->_isScopePrivate = true; $this->configProvider = $configProvider; $this->layoutProcessors = $layoutProcessors; }
it's an instance of Magento\Checkout\Block\Checkout\LayoutProcessor and is passed by di.xml inside vendor/magento/module-checkout/etc/frontend , with :
<type name="Magento\Checkout\Block\Onepage"> <arguments> <argument name="layoutProcessors" xsi:type="array"> <item name="addressFormAttributes" xsi:type="object">Magento\Checkout\Block\Checkout\LayoutProcessor</item> <item name="totalsSortOrder" xsi:type="object">Magento\Checkout\Block\Checkout\TotalsProcessor</item> </argument> </arguments> </type>
quick view on Magento\Checkout\Block\Checkout\LayoutProcessor :
we have the function process , getting attribute from attribute of customer , (address and register address), and after that mapped to form and push label :
/** @var \Magento\Eav\Api\Data\AttributeInterface[] $attributes */ $attributes = $this->attributeMetadataDataProvider->loadAttributesCollection( 'customer_address', 'customer_register_address' );</p> <p> $elements = []; foreach ($attributes as $attribute) { if ($attribute->getIsUserDefined()) { continue; } $elements[$attribute->getAttributeCode()] = $this->attributeMapper->map($attribute); $label = $elements[$attribute->getAttributeCode()]['label']; $elements[$attribute->getAttributeCode()]['label'] = __($label); } }
it's use 2 class :
\Magento\Customer\Model\AttributeMetadataDataProvider and \Magento\Ui\Component\Form\AttributeMapper
after that this function process , read configuration of component with public function process($jsLayout) , $jsLayout is our checkout_index_index.xml for mapping elements with our shipping step and billing step,
Note : as result we have form element and labels return by model (Address(entity tyep customer_address) => Magento\Customer\Model\Address\Form => from customer_address and customer_register_address) and AttributeMapper , mapped and merged with our configuration of shipping step (field and field set) inside checkout_index_index.xml (you can explore table eav_form_type_entity and eav_form_type and eav_entity_type)
Ok inside Magento\Checkout\Block\Onepage , the function getJsLayout() return the global configuration in json format :
public function getJsLayout() { foreach ($this->layoutProcessors as $processor) { $this->jsLayout = $processor->process($this->jsLayout); } return \Zend_Json::encode($this->jsLayout); }
to our text/x-magento-init component in onepage.phtml :
<script type="text/x-magento-init"> { "#checkout": { "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout();?> } } </script>
Resources from official doc :
Add a new checkout step
Customize the view of a checkout step
Add a custom payment method to checkout
Add custom validations before order placement
Add custom shipping carrier validations
Add custom input mask for ZIP code
Add a custom template for a form field on Checkout page
Add a new input form to checkout
Add a custom shipping address renderer
Comments