Magento – How to add custom layout handles programatically for Category View in Magento 2

layoutmagento2update-handle

So, I want to add a custom layout handle for all category view pages.. the handle that is supposed to be loaded depends on certain category parameters, so the handle needs to be added programmatically with $page->addPageLayoutHandles()

Seems easy..? Apparently not

Magento 2 provides a nice Plugin system that I was naturally going to use, just define a afterExecute() plugin to be run after the original category execute() and push any updates into the Page object from there.

Unfortunately, it does not quite work that way.. reason being that the original execute() method (at the very end) will execute $page->getConfig()->addBodyClass() — a call to this method will automatically force the layout to be completely loaded and generated, so any subsequent attempts to add new layout handles to Page are completely useless.
I looked around to find any not-so-elegant-ways to achieve the same thing (still using plugins).. did not find any.

I ended up running my own controller for category view, however, I would not prefer to leave it that way.

So my question is.. how can I add new page layout handles (programmatically) for category view? and do it elegantly.

Best Answer

The XML way

Well an easy way is to create the following file in your module folder: view/frontend/layout/catalog_category_view.xml with the following content:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="your_custom_handle"/>
</page>

It is not more or less elegant than the PHP way and according to what you found it is safer.

The PHP way

Unfortunately in your case, it seems like PHP is the only way to achieve dynamically create handles based on the parameters the category has.

Via plugins

Instead of creating a plugin on the execute() method of your action class, you could directly create a plugin on the addPageLayoutHandles() method of Magento\Framework\View\Result\Page

Main problem is that it will be called every time this method is called and you will have to add some conditions in your plugin code to ensure you're on a category view page.

Via preferences

Another way of doing it would be to use preferences for the category view action class:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Controller\Category\View"
                type="Vendor\Module\Controller\Category\View"/>
</config>

Then in your custom controller class you simply override the execute() method by copying/pasting the original method and add your modifications directly in this method.

Main problem is that when you will upgrade your Magento install, if changes are added to the original native Magento action class, it won't reflect in your custom action class.