Add Private Content in Magento 2

Magento’s Page Cache module successfully caches page content and is now a part of the Community Edition releases. The cached content can be split into two parts: public content and private content.

Public content is stored within the cache location you specify for your Magento application, either in the file system, in the database, or by using an external service such as Redis.

Private content is not stored server-side but on the client side only.

Magento 2 has several examples of rendering private content within a few of its modules, including:

  • module-wishlist
  • module-sales
  • module-checkout
  • module-review

To generate customer private data you need to use CustomerData sections.

To add private content, you must first define a section source. This can be defined within a module’s di.xml file.

// app/code/[Vendor]/[Module]/etc/frontend/di.xml

<?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\Customer\CustomerData\SectionPoolInterface">
        <arguments>
            <argument name="sectionSourceMap" xsi:type="array">
                <item name="custom_section" xsi:type="string">[Vendor]\[Module]\CustomerData\CustomSection</item>
            </argument>
        </arguments>
    </type>
</config>

The sections themselves are defined within a sections.xml configuration file. Here you can add a <section> to an <action>. For example, the below code adds the custom_section section to the checkout/cart/add action.

// app/code/[Vendor]/[Module]/etc/frontend/sections.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <action name="checkout/cart/add">
        <section name="custom_section"/>
    </action>
</config>

The CustomerData section defined in di.xml contains a class that implements Magento\Customer\CustomerData\SectionSourceInterface.

This class requires the getSectionData() method to be defined. Within this method, you can retrieve data from the class to a frontend template file.

// app/code/[Vendor]/[Module]/CustomerData/CustomSection.php

<?php
namespace [Vendor]\[Module]\CustomerData;

use Magento\Customer\CustomerData\SectionSourceInterface;
use Magento\Framework\DataObject;

class CustomSection extends DataObject implements SectionSourceInterface
{
    public function getSectionData()
    {
        $data = ['foo' => 'bar'];
        return $data;
    }
}

The next step requires a jsLayout component argument to be configured within the layout XML configuration file.

This is added within the pair of <block> declaration nodes.

// app/code/[Vendor]/[Module]/view/frontend/layout/default.xml

<?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>
        <referenceContainer name="before.body.end">
            <block class="Magento\Framework\View\Element\Template" name="[vendor].[module].addtocart"
                   template="[Vendor]_[Module]::[module]/addtocart.phtml">
                <arguments>
                    <argument name="jsLayout" xsi:type="array">
                        <item name="components" xsi:type="array">
                            <item name="custom_section" xsi:type="array">
                                <item name="component" xsi:type="string">[Vendor]_[Module]/js/view/custom_section</item>
                            </item>
                        </item>
                    </argument>
                </arguments>
            </block>
        </referenceContainer>
    </body>
</page>

The JavaScript component defined uses customerData.get() to get data from the section block.

In our example, customerData.get('custom_section') will return the contents of the data within the custom_section section.

// app/code/[Vendor]/[Module]/view/frontend/web/js/view/custom_section.js

define([
    'uiComponent',
    'Magento_Customer/js/customer-data'
], function (Component, customerData) {
    'use strict';

    return Component.extend({
        initialize: function () {
            this._super();
            this.custom_section = customerData.get('custom_section');
        }
    });
});

Finally, within the addtocart.phtml template, use Knockout.js syntax to render the data.

In the below example, custom_section().foo will render bar as per the data array returned within the CustomSection.php class.

// app/code/[Vendor/[Module]/view/frontend/templates/[module]/addtocart.phtml

<div data-role="custom-section" data-bind="scope: 'custom_section'">
    <span class="content-type" data-bind="text: custom_section().foo"></span>
</div>

<script type="text/x-magento-init">
{
    "[data-role=custom-section]": {
        "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout(); ?>
    }
}
</script>

This will successfully render the text bar within the <span> at the bottom of the page only when a product is added to the cart. This technique is useful for when wanting to track customer-related events in Magento.

Note: This article is based on Magento CE version 2.1.