One of the challenges Magento 2, (and all “full stack” oriented frameworks), face is data synchronization between the front end and the server. Server data will always represent the “source of truth” for any particular piece of data, but good front end developers will always be looking to reduce to number of round trips they make to the server. Magento 2 faces an additional cultural challenge in that it remains a PHP framework, and most developers doing Magento 2 work are going to rely on its many code injection features to change how server side data is saved. In other words, reducing a client side round trip may work out of the box, but may result in stale data if new server side code runs during ajax requests.
I’ve notice that, whenever a developer asks for help along these lines the mysterious sections.xml
files are often mentioned as an option. Magento’s dev docs are typically professional yet cryptic, and while there are some decent explanations of how the sections.xml
file work from a backend perspective no one seems to explain or understand the limited scope of what these configuration files can do for you.
The following is not a complete sections.xml
tutorial, but hopefully contains enough information for you to get started.
What are Magento Sections
Magento has a RequireJS module named Magento_Customer/js/customer-data
. This module implements a dictionary like object that allows you you get and save data. Setting data is easy
requirejs(['Magento_Customer/js/customer-data'], function(customerData){
customerData.set('someKey', 'some value');
});
Getting data is also easy, but has a small twist. When you fetch the data, you get a Knockout observable
requirejs(['Magento_Customer/js/customer-data'], function(customerData){
var observableObject = customerData.get('someKey', 'some value');
//call returned observable to get value
console.log(observableObject());
});
Knockout’s observables allow you to create user interfaces that dynamically update when you update the value in the observable.
Behind the scenes, Magento stores these values using both cookies and the browser’s “localStorage” mechanism. The cookie is named section_data_ids
, holds a list of each key (someKey
above), and is used as a rudimentary cache-invalidation mechanism (see below).
Values are saved into localStorage using jQuery’s localStorage API
$.initNamespaceStorage('mage-cache-storage').localStorage;
Folks conversant with Magento’s RequireJS libraries may be surprised that they’re not using the Magento_Ui/js/lib/core/storage/local
module to store values. Folks familiar with Magento’s engineering practices will be less surprised.
As you may have surmised, Magento considers each key in the Magento_Customer/js/customer-data
registry to be a “section”.
What Does sections.xml
Do?
To understand what sections.xml
files do, we first need to talk about a new RequireJS module: Magento_Customer/js/section-config
. Every front end Magento HTML page contains the following x-magento-init
script.
<script type="text/x-magento-init">
{
"*": {
"Magento_Customer/js/section-config": {
"sections": {
"stores/store/switch": "*",
"directory/currency/switch": "*",
/* ... more information ... */,
},
"clientSideSections": ["checkout-data"],
"baseUrls": ["http://magento-2-1-3.dev/"]
}
}
}
</script>
This tag is rendered by the following bit of Layout Update XML
<!-- File: vendor/magento/module-customer/view/frontend/layout/default.xml -->
<block name="customer.section.config"
class="MagentoCustomerBlockSectionConfig"
template="Magento_Customer::js/section-config.phtml"/>
The JSON object that gets passed to Magento_Customer/js/section-config
(truncated above) is a serialized representation of the data in sections.xml
. The purpose of adding this to every page is to ensure that RequireJS developers have access to this configuration information.
Next, we’ll need to take a look at the definition file for the Magento_Customer/js/customer-data
RequireJS module.
#File: vendor/magento/module-customer/view/frontend/web/js/customer-data.js
define([
'jquery',
'underscore',
'ko',
'Magento_Customer/js/section-config',
'mage/storage',
'jquery/jquery-storageapi'
], function ($, _, ko, sectionConfig, mageStorage) {
/*...*/
$(document).on('ajaxComplete', function (event, xhr, settings) {
var sections,
redirects;
if (settings.type.match(/post|put/i)) {
sections = sectionConfig.getAffectedSections(settings.url);
if (sections) {
customerData.invalidate(sections);
redirects = ['redirect', 'backUrl'];
if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) {
return;
}
customerData.reload(sections, true);
}
}
});
/**
* Events listener
*/
$(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);
}
}
});
}
/*...*/
First, notice that Magento_Customer/js/section-config
is imported as sectionConfig
. Then, look to the jQuery event listeners. The first time you load the Magento_Customer/js/customer-data
module, Magento sets up an ajaxComplete listener, and a form submit listener.
The ajaxComplete listener will, after every ajax request that’s made via jQuery, check the sectionConfig
object. If this sectionConfig
object contains a URL that matches the just requested URL, and the URL was requested via an HTTP POST
or an HTTP PUT
, then Magento will “reload” the section. Reloading a section happens in the customerData.reload
call. A reload will pass the sections, via AJAX, to the http://magento.example.com/customer/section/load
URL and use the returned JSON to reset the value in the key/section/observable.
The form submit listener will, whenever any form gets submitted, try to match the submission URL with a URL/section from sectionConfig
. If a match is found, then that section will be “invalidated”. Invalidating a section is a way of telling the Magento_Customer/js/customer-data
object that a section needs to be refreshed, without fetching it. The idea is other code will keep an eye what needs to be invalidated and decide when to update it. For those curious about implementation the details, it seems like Magento stores invalidation information in the aforementioned section_data_ids
cookie.
In plain english? Not possible. In less complicated english? If you store data in the Magento_Customer/js/customer-data
registry, and include a configuration for your key/section in sections.xml
, Magento will automatically update the values in that key/section whenever an ajax request is made to the URL configured in sections.xml
. The updated data comes from a request to the following URL
URL:
http://magento.example.com/customer/section/load/
Controller File:
vendor/magento/module-customer/Controller/Section/Load.php
The previously mentioned StackExchange and dev docs links contain information on how to configure Magento so this URL returns data from a PHP class you provide.
For example, this sections.xml
configuration
#File: vendor/magento/module-customer/etc/frontend/sections.xml
<action name="customer/ajax/login">
<section name="checkout-data"/>
<section name="cart"/>
</action>
tells Magento that, whenever the customer/ajax/login
URL is loaded via AJAX, that Magento should also update the values in the checkout-data
and cart
keys of the Magento_Customer/js/customer-data
registry. Again, updated values are loaded from the http://magento.example.com/customer/section/load
URL.
The reason you care: If Magento, or a third party developer (you), uses a section from Magento_Customer/js/customer-data
to populate a KnockoutJS or UI Component binding/dom-element, then Magento will automatically update the UI when these ajax requests are made. This automatic updating happens because these values are stored as KnockoutJS observables. If there’s other programatic logic that references a value from Magento_Customer/js/customer-data
, then that data will be updated when ajax requests are made.
This is a powerful, but limited, feature. If the code in question does not use the Magento_Customer/js/customer-data
repository to display data, then sections.xml
will do nothing for you. You’ll need to update the information in your javascript application with your own code.