In Magento 2, has anyone tried to add an update handle in "Custom Layout Update" on the category admin page? I've set the following on a category:
<update handle="my_handle_lorem_ipsum_lorem" />
And I created the following file in my current theme:
[theme dir]/Magento_Catalog/layout/my_handle_lorem_ipsum_lorem.xml
However, the update handle doesn't appear to be applied.
I know the XML directives in my_handle_lorem_ipsum_lorem.xml
are correct because they work correctly when placed in [theme dir]/Magento_Catalog/layout/catalog_category_view_id_123.xml
.
By debugging I've found that the "Custom Layout Update" attribute is handled here:
// \Magento\Catalog\Controller\Category\View::execute() [excerpt]
$layoutUpdates = $settings->getLayoutUpdates();
if ($layoutUpdates && is_array($layoutUpdates)) {
foreach ($layoutUpdates as $layoutUpdate) {
$page->addUpdate($layoutUpdate);
}
}
Tracing the source of $layoutUpdates
, I found that it ultimately gets set in _extractSettings()
of \Magento\Catalog\Model\Design
:
// \Magento\Catalog\Model\Design::_extractSettings() [excerpt]
$settings->setCustomDesign(
$object->getCustomDesign()
)->setPageLayout(
$object->getPageLayout()
)->setLayoutUpdates(
(array)$object->getCustomLayoutUpdate()
);
That pulls the entire string <update handle="my_handle_lorem_ipsum_lorem" />
from the category attribute. Does that mean it's trying to use the entire string as the handle name?
All I see is the call to addUpdate()
. There doesn't seem to be a call to any function of the Merge
class that would incorporate it into the overall layout XML.
In general, how should we add a handle in the category field "Custom Layout Update," and where and how should the file should be declared?
Best Answer
I'm trying to solve a similar issue, so I'm reading through the code pretty extensively to get to the root of this. I'll start at the end and work backward:
The call to
addUpdate()
you reference actually does end up callingaddUpdate()
on the\Magento\Framework\View\Model\Layout\Merge
class. Hold onto your seat: it's a bumpy road. The$page
variable is an instance of\Magento\Framework\View\Result
, and it inherits the definition ofaddUpdate()
from its parent classLayout
(in the same namespace). That class in turn callsaddUpdate()
on aProcessorInterface
, whose default implementation is none other than:Magento\Framework\View\Model\Layout\Merge
.So then to your question about it seeming to pass the entire XML string: you are correct, and the behavior is correct. The
Merge
class should theoretically be taking care of parsing the<update>
inside_fetchRecursiveUpdates()
and then loading the layout XML from the referenced handle.The catch is that
_fetchRecursiveUpdates()
is never called on XML chunks that are added viaaddUpdate()
. They are assumed to be ready-to-go (non-recursive) layout instructions. Layout XML from externaladdUpdate()
calls are included directly via a call to$this->asString()
at the very end ofMerge::load()
. They never pass through the inner_merge()
function which is what indirectly calls_fetchRecursiveUpdates()
.A simplistic solution would be to create a new class that extends the built-in
Merge
class and set it as the preferred implementation ofProcessorInterface
. You would then change theaddUpdate()
method to perform the same logic as_fetchRecursiveUpdates()
before saving its argument to the internal array, something like the following: