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) {

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]


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?

  • I'm running into the same problem. I have verified that the category layout update xml is actually loaded and used (I can f.e. remove blocks), but the handle update seems ignored. were you able to find a solution?
    – alternize
    Jul 29, 2016 at 23:07
  • I haven't found a fix. Try a Magento 2.1 installation, maybe it's fixed there. Jul 31, 2016 at 11:10
  • It is not, unfortunately. As a workaround, I use a plugin to manually add the layout handle after checking the currently loaded category.
    – alternize
    Aug 6, 2016 at 21:30

1 Answer 1


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 calling addUpdate() 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 of addUpdate() from its parent class Layout (in the same namespace). That class in turn calls addUpdate() on a ProcessorInterface, 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 via addUpdate(). They are assumed to be ready-to-go (non-recursive) layout instructions. Layout XML from external addUpdate() calls are included directly via a call to $this->asString() at the very end of Merge::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 of ProcessorInterface. You would then change the addUpdate() method to perform the same logic as _fetchRecursiveUpdates() before saving its argument to the internal array, something like the following:

public function addUpdate($update)
    $updateXml = $this->_loadXmlString($update);
    if (strtolower($updateXml->getName()) == 'update' && isset($updateXml['handle'])) {
    } else {
        $this->updates[] = $update;
    return $this;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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