Daan Beverdam

Developer and tech enthusiast with a background in artificial intelligence and neuropsychology.

Adding a static CMS block to the Magento 2 checkout

Adding a static CMS block to the Magento 2 checkout

Adding static CMS blocks to the checkout in Magento used to be an easy task. In Magento 2 however, the whole checkout is dynamically generated with Knockout on the client side. Adding a simple CMS block is now overly complex (which seems to be the overall theme of Magento 2). Magento now requires you to create a module that provides a Knockout variable containing the CMS block content. To prevent someone else from struggling with it, I wrote a guide on how to do this.

Creating the module

The easiest way to create a module skeleton from the command line is to use n98-magerun. Get it from here if you don't already have it. Create the module in your Magento 2 root folder and change directory using the following commands:

n98 dev:module:create VendorName ModuleName
cd app/code/VendorName/ModuleName/

Replace vendor and module names to something cool if you like. I'll assume app/code/VendorName/ModuleName/ as your working directory from now on. I had some problems with the empty etc/event.xml file, so I'd suggest you just remove it.

Creating the model

First thing we'll do is create a new model. Create and open the file Model/ConfigProvider.php. Its contents should be:

<?php

namespace VendorName\ModuleName\Model;


use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\View\LayoutInterface;

class ConfigProvider implements ConfigProviderInterface
{
    /** @var LayoutInterface  */
    protected $_layout;
    protected $cmsBlock;

    public function __construct(LayoutInterface $layout, $blockId)
    {
        $this->_layout = $layout;
        $this->cmsBlock = $this->constructBlock($blockId);
    }

    public function constructBlock($blockId){
        $block = $this->_layout->createBlock('Magento\Cms\Block\Block')
                 ->setBlockId($blockId)->toHtml();
        return $block;
    }

    public function getConfig()
    {
        return [
            'cms_block' => $this->cmsBlock
        ];
    }
}

This model will create a CMS block upon calling the __construct function. The $blockID argument will determine what block it will load. This can be both the (numeric) block ID or the identifier.

Constructor arguments

Next we're going to create and open the file: etc/frontend/di.xml. Its contents should be:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Model\CompositeConfigProvider">
        <arguments>
            <argument name="configProviders" xsi:type="array">
                <item name="cms_block_config_provider" xsi:type="object">VendorName\ModuleName\Model\ConfigProvider</item>
            </argument>
        </arguments>
    </type>
    <type name="VendorName\ModuleName\Model\ConfigProvider">
        <arguments>
            <argument name="blockId" xsi:type="string">checkout_block</argument>
        </arguments>
    </type>
</config>

Here we're telling Magento that it has to load our ConfigProvider model and should use the value 'checkout_block' for the $blockId argument in its __construct function. You can now create a CMS block from the backend with identifier name checkout_block or alternatively, change the value to an already existing block identifier.

Calling the CMS block

At this point, you can use paste the following in one of the checkout template files and the the CMS block will appear:

<div data-bind="html: window.checkoutConfig.cms_block"></div>

However, this isn't a very portable solution since it requires you to edit or override the theme files. That's why we're going to create this layout file: view/frontend/layout/checkout_index_index.xml. It should contain the following:

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" 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="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="cms-block" xsi:type="array">
                                            <item name="component" xsi:type="string">VendorName/ModuleName/js/view/cms-block</item>
                                            <item name="sortOrder" xsi:type="string">0</item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

This layout file defines a new step in the checkout called cms-block that is rendered by the VendorName_ModuleName/js/view/cms-block view. I've set the sort order to 1 in this example so the block appears above the shipping address fields, but you can choose a higher number to move it down if you like.

Next, we're going to create the javascript view we've just defined: view/frontend/web/js/view/cms-block.js. It should contain this:

define(
    [
        'jquery',
        'ko',
        'uiComponent'
    ],
    function(
        $,
        ko,
        Component
    ) {
        'use strict';
        return Component.extend({
            defaults: {
                template: 'VendorName_ModuleName/cms_block'
            },

            initialize: function () {
                var self = this;
                this._super();
            }

        });
    }
);

This view function renders the VendorName_ModuleName/cms_block when called, so let's create it: view/frontend/web/template/cms_block.html. This template should contain the cms block variable we created earlier:

<div data-bind="html: window.checkoutConfig.cms_block"></div>

And voila! Your CMS block should now be visible at the top of the Magento 2 checkout. If anything went wrong, please ensure that you have the following directory structure:

app
└── code
    └── VendorName
        └── ModuleName
            ├── Model
            │   └── ConfigProvider.php
            ├── etc
            │   ├── config.xml
            │   ├── crontab.xml
            │   ├── di.xml
            │   ├── frontend
            │   │   └── di.xml
            │   └── module.xml
            ├── registration.php
            └── view
                └── frontend
                    ├── layout
                    │   └── checkout_index_index.xml
                    └── web
                        ├── js
                        │   └── view
                        │       └── cms-block.js
                        └── template
                            └── cms_block.html

If you want to review any of files used in this guide, or just install the module and be done with it, please checkout this Github page.