50

I recently came accross a new concept in Magento 2 that I found interesting: customer sections

Some of you may have notice the presence of sections.xml files that look like this:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <action name="sales/guest/reorder">
        <section name="cart"/>
    </action>
    <action name="sales/order/reorder">
        <section name="cart"/>
    </action>
</config>

From what I understood, those files specify which customer sections should be updated when the corresponding action is called.

I noticed for instance with the Magento/Checkout/etc/frontend/sections.xml the following part:

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

Is what triggers the minicart update after you have added a product to the cart.

I tried to create a custom module with the following etc/frontend/sections.xml file to test that feature:

<?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/index">
        <section name="cart"/>
    </action>
</config>

But it does not seem to try to update my cart section when I reach the cart page (no GET request in the console). It seems like this whole section functionality is handled by the Magento_Customer module in some way.

  • What are exactly those sections ? How do you define a section ?
  • How are the section updates triggered ?
  • (Optional) How can I fix my test code to update the minicart when I reach the cart page ?
84

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)
  • Awesome thanks for that. Any way you can tell why the code in my question does not refresh the mini cart when I reach the cart page ? – Raphael at Digital Pianism Oct 31 '16 at 11:10
  • 1
    @RaphaelatDigitalPianism, I've updated my comment with answer – Volodymyr Kublytskyi Oct 31 '16 at 19:27
  • I'm making a custom ajax call in cart page, i don't need this customer load section call. How can I avoid this? magento.stackexchange.com/questions/156425/… – seeni Feb 17 '17 at 11:11
5

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); 
        } 
    } 
});
  • 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); } } }); – lemk0 Jun 2 '16 at 8:22
3

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])
        );
    }
}
  • Raphael, my sections.xml isn't even attempting to update the cart when I send a post request to the url in the file... Any ideas? – LM_Fielding Oct 24 '16 at 12:41
  • @LM_Fielding yes I had the same people, read my answer – Raphael at Digital Pianism Oct 24 '16 at 12:42
  • So to get it to work, we have to write this? Is the default behaviour broken or am I misunderstanding? – LM_Fielding Oct 24 '16 at 12:43
  • @LM_Fielding well I don't know that's why I asked this question and I didn't get any good answer about it. As I said this is the "hacky" way I found to do it. – Raphael at Digital Pianism Oct 24 '16 at 12:43
  • It was definitely using a relative url for me - it doesn't trigger the section update. – LM_Fielding Oct 24 '16 at 13:32
0

I have faced same problem as author of question. After few hours of researching and dagging in documentation and core code, i suddenly got solution. In my case i got .../etc/frontend/sections.xml file with

<?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="roadsignconf/index/addtocart">
        <section name="cart"/>
    </action>
</config>

And it didn't want to work. After reading this topic and this issue https://github.com/magento/magento2/issues/3287 i was so confused that started to experiment. For me helps adding slashes:

 <?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="/roadsignconf/index/addtocart/">
            <section name="cart"/>
        </action>
    </config>

Hope it will help someone to spent less time for finding solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.