Magento 2.x
Checkout page request flow
Ramesh Lagisetty

Magento 2.x becoming more powerful and playing ambidextrous role while serving the needs for e-commerce applications.

This lockdown pushed me to take an arduous expedition to Magento 2.x landscape. After all sightseeing, I would like to share you the breadcrumbs for typical flow of Magento 2.x checkout. I wish, this may help you on your M2 questing.

The journey get kick-start with URL " http://localhost/symphisys/checkout".

Let's zoom-in the URL.

HTTPHttp protocol helps to have the communication between client (Browser) and server for request / response transactions.
host Pointes to mechine's unique address
80 Web server port
symphisys It's a context path to refer Magento project.
checkout Magento module name

Magento project execution starts with the path "/symphisys/checkout". For a quick reference, below shared an elegant request sequence flow diagram, It may give you closer look on high level overview of complex Magento 2.x checkout flow.

    http://host:80/symphisys/checkout
  • index.php
  • bootstrap.php
  • http.php
  • front controller.php
  • onepage.php
  • page.php
  • index.php onepage.php action.php
  • base.php
  • layout.php
  • layout processor.php
  • php.php
  • template.php
  • require.js
  • onepage.phtml
  • jsonhextag.php
  • render.php
  • jquery.js
  • main.js
  • scripts.js
  • app.js
  • loader.js
  • engine.js
  • knockout.js
  • layout.js
  • render.js
  • i18.js
  • shipping.html
  • form.html
  • input.html
  • field.html

I have articulated below all primary involved components.

Index.PHP

  • Web server invokes the Index.PHP file.
  • Index.PHP is the entry point to load the application.
  • The primary role is to initialize the Bootstrap class to handle request / response transaction.
  • You can find the Index file under Magento project. In our case our Magento project name is ‘symphisys’.

Bootstrap.PHP

Bootstrap having significant role to execute below logics.

  • Sets the reported errors and warnings. Errors will be displayed at front end, If only you uncomment the line -> #ini_set ('display errors', 1);
  • Disables the ‘phar’ feature to avoid the archiving application.
  • Validates the PHP version for Magento2.x. If it finds version mismatch, error shown to end user.
  • Encapsulate the variables like [SYSTEM/ REMOTE/ CONTEXT/ HTTP / SERVER/ REDIRECT/ PHP/ PATH]. key - value pair set to $_SERVER object.
  • Auto loads the Magento 2.x OOTB [Modules from vendor folder] and custom modules [Symphisys specific modules].
  • Initializes the Object manager [OM] class.
  • Load the entries from DI.xml across the modules and wrap it to the config object for future usage.
  • Starts the Profiler class to trace the application performance.
  • Initializes the error handler to catch the exceptions while program compilation.
  • Checks the application maintenance status.
  • Checks the Magento installation success status.
  • Dispatch the request to HTPP class.
  • Sets the default time zone to UTC.
Dependency Injection (DI)
  • Dependency Injection, The most tossed word from Magento 2.x on internet.
  • There are around 2000 Magento OOTB classes are registered in DI.xml for their object creation at runtime and there are more included to customize the class / method behavior. This may results, around 80% of the class steering happening through the DI.xml.
  • It briefs how much Magento having the dependency on DI 😊
What is Dependency Injection [DI]?

Dependency Injection is more over like a design pattern and it’s about an organized play between the classes to serve the needs for each other and which helps to maintain the application performance symphony.

In simple words:

If ‘customer class’ has dependency on ‘Address class’ to get the address data: This is Dependency
If your instantiating the ‘Address class’ in ‘customer class’ at constructor / method / field level: This is Injection

All together it’s dependency injection or dependency – object – injection.

There are 2 types of injections

Constructor dependency Injection

Example :
public function __construct(\Magento\Quote\Api\CartRepositoryInterface $quoteRepository) { $this->quoteRepository = $quoteRepository;}

Automatic dependency Injection

Example :
$checkoutHelper = $this->_objectManager->get(\Magento\Checkout\Helper\Data::class);

DI.xml role?

DI.xml role here to add dynamics to dependency injection. With DI particularly you can steer the class / method behavior without changing its originality and below are other features,

  • Class in-heritance.
  • Passing method arguments.
  • You can declare the proxy classes to load at constructor.
  • Create plug-ins (Interceptors) to write pre / post method code.
Why should DI?

In-general, High-level applications like Magento, built-in with complex logic with creating hundreds of modules (Magento 2.x having around 400 hundred modules). It’s very difficult to maintain the customizations / enhancements without disturbing the existing code. So, this complexity anticipates the need of design patterns. Dependency injection is popular among the design patterns, with help of DI, you will get all below benefits.

  • It helps to create an abstraction layer that de-couples the high-level and low-level modules from each other.
  • New changes will not lead to revamp the modules.
  • Easy control over the module customization.
  • Avoids the duplicate coding.
  • Reusability
  • Performance
Object Manager [OM]

Object manager concept in Magento 2.x is much like a factory design pattern. OM’s responsibility is to create the instance for requested class.

It creates two types of instances,

  • Global scope instance like a singleton: only one instance get created to serve the all requests.
  • Request scope instance: for every request, new instance will get created.

HTTP.PHP

HTTP job is to choose the area code based on the ‘Front Name’ value.

  • Magento project grouped with multiple modules and labeled with below list of area codes. Each Area code represents the target area of execution for the request processing.
  • Area CodeTarget area of execution
    frontend storefront modules
    adminhtml admin dashboard modules
    crontab crontab modules
    webapi_rest REST API code
    webapi_soap SOAP API code
  • “Front Name” value available in request path info.
  • HTTP class fetches the area-code based on the “Front Name” value. If the request comes through the web store [webroot: index.php] and “Front Name” value not matches to ‘rest’ or ‘soap’ or ‘front name resolver’ then It picks the ‘frontend’ as the area code as default. For checkout page “Front Name” value is ‘checkout’.
  • Pass the request to Front Controller and gets the response.

FrontController.PHP

Front controller job is to pick the right router class for given request.

  • It iterates the below routers list and pick the right router which matches with the data in request. For checkout landing page, it picks the Base router.

    [robots = Magento\Robots\Controller\Router],
    [urlrewrite = Magento\UrlRewrite\Controller\Router],
    [standard = Magento\Framework\App\Router\Base],
    [cms = Magento\Cms\Controller\Router],
    [default = Magento\Framework\App\Router\DefaultRouter]

  • Passes the request to Base router class.

Base.PHP

Base router class job is to create the controller path and dispatch the request to controller class.

  • It forms the controller path with filling the below parameters.

    a. [ModuleFrontName =checkout] => ModuleFrontName value gets from the URL path.
    b. [ActionPath =index] => ActionPath value gets from the Default Path component.
    c. [ActionName =index] => ActionName value gets from the Default Path component.

  • Create an action instance for path ‘checkout/index/index’.
  • Populate the instance with all necessary data. For reference mentioned below few of the values from action instance. From this point, compilation takes the momentum to prepare the response.

    • request
    • response
    • objectmanager
    • view
    • accountmanagement
    • customerrepository
    • layoutfactory
    • quoterepository
    • resultjsonfactory
    • resultlayoutfactory
    • resultpagefactory
    • resultrawfactory
    • scopeConfig
    • formKeyValidator
    • translateInline
    • coreRegistry
    • accountManagement
    • customerRepository
    • customerSession
    • context

  • Call to ‘dispatch’ method at onepage.php. It’s the super class for checkout index controller.

Index.PHP / onepage.PHP / Action.PHP

Index is an Action class which works as controller here in MVC. Below are the following operations.

  • Checks the cart status with conditions like, empty cart / error cart / min-amount. If any condition matches, it redirects to cart page.
  • Checks the user login status. Guest checkout or authorized checkout?
  • Load the order and customer data to checkout and customer session objects.
  • Initializes the Page object (Framework\View\Result\Page).

Page.PHP

Page class is the gateway to mills the checkout page data.

  • Page invokes the builder class to tailor the master layout by appending all the dependency layouts from below modules.

    • /module-catalog-search/view/frontend/layout/
    • /module-checkout/view/frontend/page_layout/
    • /theme-frontend-luma/Magento_Theme/layout/
    • /module-checkout/view/frontend/layout/
    • /module-catalog/view/frontend/layout/
    • /module-search/view/frontend/layout/
    • /module-bundle/view/base/layout/

  • Layouts get loaded with the help of handles. Handle represents the layout xml file. Checkout master layout created with help of below handles.

    • default
    • default_head_blocks
    • checkout_index_index
    • checkout_cart_sidebar_total_renderers
    • checkout_cart_sidebar_item_renderers
    • checkout_cart_sidebar_item_price_renderers

  • Layout class traverse through the master layout nodes with help of Classes [Generatorpool / Readerpool / Blocks / Container / Header / uiElement ] to create a map. Map contains the key - value pair of layout nodes and blocks. For the reference, shared below the map data for checkout page. Below are the blocks behind the checkout page to prepare and fetch the data.
  • Layout Nodes Associated Blocks
    checkout.root \Checkout\Block\Onepage
    head.additional \Framework\View\Element\Template
    head.components \Framework\View\Element\Js\Components
    require.js \Framework\View\Element\Template
    requirejs-config \RequireJs\Block\Html\Head\Config
    js_cookies \Framework\View\Element\Js\Cookie
    global_notices \Theme\Block\Html\Notices
    skip_to_content.target \Framework\View\Element\Template
    logo \Theme\Block\Html\Header\Logo
    page.main.title \Theme\Block\Html\Title
    copyright \Theme\Block\Html\Footer
    absolute_footer \Theme\Block\Html\Footer
    formkey \Framework\View\Element\FormKey
    authentication-popup \Customer\Block\Account\AuthenticationPopup
    customer.section.config \Customer\Block\SectionConfig
    customer.customer.data \Customer\Block\CustomerData
    customer.data.invalidation.rules \Customer\Block\CustomerScopeData
    product.price.render.default \Framework\Pricing\Render
    frontend-storage-manager \Catalog\Block\FrontendStorageManager
    sales_page_head_components \Framework\View\Element\Js\Components
    checkout_page_head_components \Framework\View\Element\Js\Components
    cookie_notices \Cookie\Block\Html\Notices
    cookie_config \Framework\View\Element\Js\Cookie
    google_analytics \GoogleAnalytics\Block\Ga
    logger \Ui\Block\Logger
    theme.active.editor \Ui\Block\Wysiwyg\ActiveEditor
    google.verification \Framework\View\Element\Template
    google.gtag.global \Framework\View\Element\Template
    google.gtag.cart \Framework\View\Element\Template
    pagecache_page_head_components \Framework\View\Element\Js\Components
    pageCache \PageCache\Block\Javascript
    bundle_checkout_page_head_components \Framework\View\Element\Js\Components
    reports_page_head_components \Framework\View\Element\Js\Components
    newsletter_head_components \Framework\View\Element\Js\Components
    translate-config \Translation\Block\Html\Head\Config
    wishlist_page_head_components \Framework\View\Element\Js\Components
    amazon_config \Amazon\Core\Block\Config
    emailCapture \Framework\View\Element\Template
    ddg.checkout.tag \Dotdigitalgroup\Email\Block\Tracking
    signifyd.fingerprint \Signifyd\Block\Fingerprint
  • Page having the other dependency classes like [Layout.php / php.php / template.php / Render.php/ JsonHexTag.php] to render the response.

    • Layout.php -> read the layout xml file
    • Renderer.php -> Render the page assets like, Meta tags/Page title/JS/CS/ Fevicon.
    • Template.php -> fetch the phtml for the assigned block
    • PHP.php -> Render the block response in phtml
    • JsonHexTag.php -> response converted to JSON object

  • Load the onepage.phtml which is the template for the checkout landing page.

Onepage.PHP

Onepage.PHPis a block and has a responsibility to fetch the data for checkout page’s body content. Onepage.phtml has triggers to invoke methods in onepage.PHP class.

  • Onepage block render the layout-processors to get the data of,

    • Shipping
    • Billing
    • Shipping method
    • Country
    • State
    • Cart summary
    • Tax
    • Payment methods
    • Authentication
    • Progress-Bar

  • Below is the list of layout processors involved
  • \Checkout\Block\Checkout\LayoutProcessor Fetch the shipping / billing addresses details for the customer
    \Checkout\Block\Checkout\TotalsProcessor Get the cart total details
    \Checkout\Block\Checkout\DirectoryDataProcessor Get the country / state details
    \ReCaptcha\Block\LayoutProcessor\Checkout\Onepage Get the captcha details
    \PaypalReCaptcha\Block\LayoutProcessor\Checkout\Onepage Get the pay pal payment details
    \Shipping\Block\Checkout\LayoutProcessor Get the shipping method details
    \Tax\Block\Checkout\SortTaxMessageProcessor Get the tax details
  • Response sent back to the onepage.phtml.
  • Extracting JSON object will be taken care by the RequireJS / underscoreJS / Jquery / Knockout framework from onepage.phtml.

Onepage.PHTML

Onepage.phtml will do the following operations.

  • Initiate the onepage.php block.
  • Init the scope variable as ‘checkout’ at data-bind="scope:'checkout'" tag.
    <div id="checkout" data-bind="scope:'checkout'" class="checkout-container" > </div>
  • "text/x-magento-init" is custom type java script attribute which is belongs to Magento 2.x.
  • <script type="text/x-magento-init"> {
    "#checkout": {
           "Magento_Ui/js/core/app": <?= / @noEscape / $block->getJsLayout() ?>
            }
    }
    
  • Above is the piece of code to trigger the getJsLayout() method in onepage.php block.
  • It returns the response JSON object and assigned to ‘#checkout’ variable.
  • ‘#checkout ‘can be referred as root level ‘view model’ object. It has the details related to the nodes and associated JS components.
    It looks like below.
  • Window.checkoutConfig variable get loaded with customer / active cart / shipping freights data.
  • Let’s have the closer look on, how the ‘checkout’ data get extracted through Requirejs / Jquery / KO / UnderscoreJS libraries in checkout shipping page.
JS libraries used in Magento 2.x
1. Require JS

If you’re building a high scale application and having the requirement to use multiple JavaScript libraries all together, like KO / JQuery / underscoreJS / nodeJS / ReactJS / BackboneJS / AngularJS, and

  • If you need a platform to do asynchronous file loading,
  • to organize the call back functions,
  • wanted to maintain the co-ordination between JS components,
  • wanted to data exchange between the JS components,
  • set the right performance for the application,
  • to avoid the head script include in phtml,

Then, the answer is RequireJS.

RequireJS-Config.js
  • Requires-config.js is a piece of design from requireJS library.
  • Each module can have its own Requires-config.js file.
  • It has the entries for all JS files which are involved in particular module implementation.
  • It has a specification to entry the files with Key – value pair.
    Example :
    'map': {
    '*': {
    'ko': 'knockoutjs/knockout',
  • Above example, ‘KO’ is a key for the value of knockout.js. Other JS components can refer knockout.js with key as ‘KO’.
2. Underscore JS

UnderscoreJS library provides the utility functions like foreach / isempty / duplicheck / invoke / index / String operations / and more like this. You need not write you’re won utility JS functions for your project. Simply you can take the help from the underscoreJS library. It has all the utility methods which you can use for your requirements.

3. JQery

You knew much better 😉

4. Knockout JS

KnockoutJS is another JS library which is having mechanism to implement the headless architecture. KO follows the MVVM pattern, which decouples the presentation layer and business layer. Let’s relate the components to V-MV-M [MVVM] on particularly checkout landing page.

M Business logic layer [models / resource models / blocks] represents model.
V Onepage.phtml represents the view.
V .html represents view. Knockout uses .html template while render the response.
M Scope variable represents the Model. Scope variable holds the response object received from the onepage.php block.

Main.JS

Main.JS will do following operations.

  • Scan the HTML document and get all the response JSON objects for checkout page. Below for your reference.
  • Node Content Sections
    cookies1. domain
    2. expires
    3. lifetime
    4. path
    5. secure
    authenticationPopup 1. authenticationPopup
    section-config1. baseUrls
    2. clientSideSections
    3. sectionNames
    4. sections
    customer-data1. cart
    2. persistent
    invalidation-processor 1. invalidationRules
    pageCache1. handles
    2. originalRequest
    3. URL
    4. versionCookieName
    checkout1. Authentication
    2. Progress Bar
    3. Estimation
    4. Messages
    5. Steps
    6. Sidebar
    storage-manager1. product_data_storage
    2. recently_compared_product
    3. recently_viewed_product
  • Further it will drill down and stitch the response Objects to data-bind variables on checkout page. Below for your information.
  • HTML element Bind variable Response Object
    div#authenticationPopupdata-bind="scope:'authenticationPopup'" authenticationPopup
    div#checkout.checkout-container data-bind="scope:'checkout'" checkout
  • Main.JS passes the data-bind objects to app.JS for the compilation.

app.JS

  • Set the types values into store object. FYR below.
  • ‘Types’ value from Respone Which has a map in ‘checkout_index_index.xml’
    form.input
        
      <item name="types" xsi:type="array">
        <item name="form.input" xsi:type="array">
          <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>
            <item name="config" xsi:type="array">
              <item name="provider" xsi:type="string">checkoutProvider</item>
              <item name="template" xsi:type="string">ui/form/field</item>
            <item name="elementTmpl" xsi:type="string">ui/form/element/input</item>
          </item>
        </item>
      </item>
    
  • App.JS passes the response object to layout.JS.

layout.JS

layout JS perform the following operations:

  • Traverse through the ‘#checkout’ response object.
  • Create the nested nodes and assign the required data by extracting the response object.
  • Sample node is like below.

    children:
    flatrate-rates-validation:
    component: "Magento_OfflineShipping/js/view/shipping-rates-validation/flatrate"
    __proto__: Object
    __proto__: Object
    componentType: undefined
    dataScope: ""
    extendProvider: true
    index: "shipping-rates-validation"
    initChildCount: 1
    name: "checkout.steps.shipping-step.step-config.shipping-rates-validation"
    parentName: "checkout.steps.shipping-step.step-config"
    parentScope: ""
  • Node will be set to the registry.JS. This component works like a bean to store the data as key – value pair and helps to exchange the data between JS components. Sample data populated Key-value pair below. Altered the value for precise.
  • Key value
    checkout.steps.shipping-step.shippingAddress.shipping-address-fieldset.firstnamecomponent:
    "Magento_Ui/js/form/element/abstract"
    customScope: "shippingAddress"
    dataScope: "shippingAddress.firstname"
    elementTmpl: "ui/form/element/input"
    error: ƒ observable()
    errorId: "error-LS8LHFT"
    index: "firstname"
    initialValue: "Ramesh"
    inputName: "firstname"
    input_type: "input"
    label: "First Name"
    labelVisible: true
    links: {value: "checkoutProvider:shippingAddress.firstname"}
    listens: {visible: "setPreview", value: "setDifferedFromDefault", isUseDefault: "toggleUseDefault", checkoutProvider:data.reset: "reset", checkoutProvider:data.overload: "overload", …}
    name: "checkout.steps.shipping-step.shippingAddress.shipping-address-fieldset.firstname"
    parentName: "checkout.steps.shipping-step.shippingAddress.shipping-address-fieldset"
    parentScope: "shippingAddress"
    provider: "checkoutProvider"
  • Control passes to Knockout JS.

Knockout.JS

Knockout is a java script library. It plays in Magento a sophisticated role on field of view layer. The target mission is to compile the view model object.

What Knockout does here!

On high-level view

  • Control over the HTML elements like, models / form / fields / attributes / CSS / JS.
  • Knockout uses JS components like Engine / Loader / Render /I18 to execute below actions.
loader.JS

Loader JS prepare the complete path for .html based on the requested path.

Example:
Requested path: Magento_Checkout/summary/cart-items
Prepared template path: /template/summary/cart-items.html

render.JS

Render JS load the html with content.

cart-items.html

    
<div class="block items-in-cart" data-bind="mageInit: 
{'collapsible':{'openedState': 'active', 'active': isItemsBlockExpanded()}}">
  <div class="title" data-role="title">
    <strong role="heading" aria-level="1">
      <translate args="maxCartItemsToDisplay" if="maxCartItemsToDisplay 
      <getCartLineItemsCount()"/>
      <translate args="'of'" if="maxCartItemsToDisplay <getCartLineItemsCount()"/>
      <span data-bind="text: getCartSummaryItemsCount()"></span>
      <translate args="'Item in Cart'" if="getCartLineItemsCount() === 1"/>
      <translate args="'Items in Cart'" if="getCartLineItemsCount() > 1"/>
    </strong>  
  </div>
  <div class="content minicart-items" data-role="content">
    <div class="minicart-items-wrapper overflowed">
      <ol class="minicart-items">
        <each args="items()">
          <li class="product-item">
            <div class="product">
            <each args="$parent.elems()" render=""/>
            </div>
          </li>
        </each>
      </ol>
    </div>
  </div>
  <div class="actions-toolbar" if="maxCartItemsToDisplay < getCartLineItemsCount()">
    <div class="secondary">
      <a class="action viewcart" data-bind="attr: {href: cartUrl}">
        <span data-bind="i18n: 'View and Edit Cart'"></span>
      </a>
    </div>
  </div>
</div>	
  • Knockout read or alters the bindings on .html template. Binding is like a place holder in HTML. Using placeholder’s KO control the data on html template at runtime.
  • For below example, It reads the binding here,
  • <span data-bind="text: getCartSummaryItemsCount()"> </span>
  • Initializes binding context (view model) for the particular node.
  • ‘View model’ object populated with output values from the assigned JS component of the node.
  • Here KO gets the ‘cart items summary count’ from the ‘/summary/cart-items.js’ component.
  • JS component get the output values from the windows.checkoutconfig object. Flow is like this.
    ‘viewmodel’ -> cart-items.js -> window.checkoutconfig JS -> Quotedata -> items_qty -> 2
  • Below is the end point at KO script to render the output as ‘2’ on page.
  •     setDomNodeChildren: function (domNode, childNodes) {
          ko.utils.emptyDomNode(domNode);
          if (childNodes) {
            for (var i = 0, j = childNodes.length; i < j; i++)
              domNode.appendChild(childNodes[i]);
          }
        } 
    
  • domeNode refers -> <span data-bind="text: getCartSummaryItemsCount()"> </span>
  • childNodes -> it’s an array object. ‘nodeValue’ attribute holds the output value as 2.

i18.JS

I18 component is an end point in control flow to show the static content in checkout page. Content is like, form lables / headings etc.

'Item in cart' value prints from method 'setText[ ]' i18.JS'2' value prints from method 'setDomNodeChidren[ ]' at knocout.JS

html flow for the shipping address form

onepage.phtml -> onepage.html -> shipping.html -> form.html -> field.html -> input.html

The above cycle will be the same for execution of other sections in the page.

Thank You 👍

Share
Tags
Categories

Ramesh Lagisetty
Founder & CEO
Symphisys