Magento 2: how do customer sections / sections.xml work?

What are exactly those sections ?

A section is a piece of customer data grouped together. Each section is represented by key that is used to access and manage data and data itself. Magento loads sections by AJAX request to /customer/section/load/ and caches loaded data in the browser local storage under the key mage-cache-storage. Magento tracks when some section is changed and load updated section automatically.

How do you define a section ?

A section defined in di.xml file by adding a new section into sections pool

<type name="Magento\Customer\CustomerData\SectionPoolInterface">
    <arguments>
        <argument name="sectionSourceMap" xsi:type="array">
            <item name="cart" xsi:type="string">Magento\Checkout\CustomerData\Cart</item>
            <item name="directory-data" xsi:type="string">Magento\Checkout\CustomerData\DirectoryData</item>
        </argument>
    </arguments>
</type>

So here two new sections are registered cart and directory-data. Magento\Checkout\CustomerData\Cart and Magento\Checkout\CustomerData\DirectoryData implements Magento\Customer\CustomerData\SectionSourceInterface and provides actual data as result of getSectionData method.

How are the section updates triggered ?

Magento assumes that customer's private data is changed when a customer sends some state modification request (POST, PUT, DELETE). To minimize the load on server, developers should specify which action (or request) updates which customer data section in etc/section.xml.

<action name="checkout/cart/add">
    <section name="cart"/>
</action>

Action name is an action key pattern. When a user calls to action that matches specified pattern Magento will detect that corresponding section is outdated and loads it again. If action name is * that means that section will be updated on each POST and PUT request. If section tag is missed then all section will be updated.

So conceptually this is wrong to update mini cart when you rich cart page. At this point, mini cart (or cart section) already should be updated.

You can find more information about Customer Data here


Internal Implementation

To understand when and how sections are updated let's see implementation. Key to understanding are files magento2ce/app/code/Magento/Customer/view/frontend/web/js/section-config.js and magento2ce/app/code/Magento/Customer/view/frontend/web/js/customer-data.js.

At the end of last one of the two events handlers are registered for ajaxComplete and submit. That means that when any form is posted (with POST or PUT methods) to server, or when JavaScript sends an AJAX, POST or PUT request, the handlers will be invoked. Both handlers have similar logic: with help of Magento_Customer/js/section-config check should be any section updated or not. If some section should be updated then customerData.invalidate(sections) is called. And later all invalidated sections are loaded from a server.

So how does Magento_Customer/js/section-config know which section should be removed and on which action? The answer is in Magento/Customer/view/frontend/templates/js/section-config.phtml:

<script type="text/x-magento-init">
<?php
     /* @noEscape */ echo $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode([
    '*' => ['Magento_Customer/js/section-config' => [
        'sections' => $block->getSections(),
        'clientSideSections' => $block->getClientSideSections(),
        'baseUrls' => array_unique([
            $block->getUrl(null, ['_secure' => true]),
            $block->getUrl(null, ['_secure' => false]),
        ]),
    ]],
]);
?>
</script>

In such way, a server passes merged sections configuration to a browser.

So assuming all of that, section may be updated only by POST or PUT form submitting or AJAX request

In addition, there are only two notes:

  • all described here is internal implementation and may be changed, so you may safely use only sections.xml and expect section updates when specified POST or PUT or DELETE actions are triggered.
  • if you sure, that you really need update some section you can always do something like this: require('Magento_Customer/js/customer-data').reload(['cart'], false)

Action that you defined in tag should evoked through POST request. e.g:

Also if you want refresh customer data in all sections, just use (Look at vendor/magento/module-customer/etc/frontend/sections.xml)

You can also look at the end of file vendor/magento/module-customer/view/frontend/web/js/section-‌​config.js
Find the code:

$(document).on('submit', function (event) { 
    var sections; 
    if (event.target.method.match(/post|put/i)) { 
        sections = sectionConfig.getAffectedSections(event.target.action);
        if (sections) { 
            customerData.invalidate(sections); 
        } 
    } 
});

A hacky way I found to do that:

In my action class that redirects to the cart I do:

$this->_checkoutSession->setMinicartNeedsRefresh(true);

Then I've added the following to my cart page:

<?php if ($this->isRefreshRequired()): ?>
    <script type="text/javascript">
        require( [ 'jquery' ], function ($)
        {
            $.ajax({
                url: '<?php echo $this->getRefreshUrl(); ?>',
                type: 'POST'
            });
        });
    </script>
<?php endif; ?>

Then in my block I have:

public function isRefreshRequired()
{
    if ($this->_checkoutSession->getMinicartNeedsRefresh()) {
        $this->_checkoutSession->setMinicartNeedsRefresh(false);
        return true;
    } else {
        return false;
    }
}

/**
 * Get the refresh URL
 * @return string
 */
public function getRefreshUrl()
{
    return $this->getUrl('module/cart/refresh');
}

And my Refresh.php action class looks like this:

<?php

namespace Vendor\Module\Controller\Cart;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;

class Refresh extends Action
{

    /**
     * Dummy action class called via AJAX to trigger the section update
     */
    public function execute()
    {
        return $this->getResponse()->representJson(
            $this->_objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode(['success'=>true])
        );
    }
}