Recently, while working on a Magento 2 site, I needed to be able to change the layout for products in a specific category. While there are a few ways to do this, such as a design update on the product page, I couldn’t find an easy way to do it via a layout xml update.
For categories, you can just created a modified layout file, like this, where ‘{CATEGORY_ID}’ is the numeric category id: catalog_category_view_id_{CATEGORY_ID}.xml
However, products do not support creating a product view template based on category, only product id(catalog_product_view_id_{PRODUCT_ID}.xml) and product type(catalog_product_view_type_{PRODUCT_TYPE}.xml).
It turns out, it is not very difficult to add support for this, by modifying the initProductLayout function of the \Magento\Catalog\Helper\Product\View class. You can find the default class here: vendor/magento/module-catalog/Helper/Product/View.php
This is a two step process, after which, you will be able to use layout handles to customize the product view, based on the category, like this: catalog_product_view_category_{CATEGORY_ID}.xml
So, your layout might look like this: app/code/Company/Plugin/view/frontend/layout/catalog_product_view_category_1.xml
And then after that, any product that is in category #1, will get updated using this layout file.
This assumes you already have a Magento plugin working and are comfortable making code changes and was tested on a Magento 2.3 site.
Before doing anything, make a backup of your files and database.
No really, backup your files and database and make sure you are comfortable reverting from a backup. If you are not, you should not continue.
First, we will create a new helper in your plugin: app/code/Company/Plugin/Helper/Product.php
Add the below and make sure to change the namespace, ‘Company\Plugin’, accordingly:
<?php namespace Company\Plugin\Helper\Product; use Magento\Framework\View\Result\Page as ResultPage; class View extends \Magento\Catalog\Helper\Product\View { public function initProductLayout(ResultPage $resultPage, $product, $params = null) { $settings = $this->_catalogDesign->getDesignSettings($product); $pageConfig = $resultPage->getConfig(); if ($settings->getCustomDesign()) { $this->_catalogDesign->applyCustomDesign($settings->getCustomDesign()); } // Apply custom page layout if ($settings->getPageLayout()) { $pageConfig->setPageLayout($settings->getPageLayout()); } $urlSafeSku = rawurlencode($product->getSku()); /* Get categories from product and load into array */ $category_ids = $product->getCategoryIds(); $category_ids_clean = array(); if(!is_array($category_ids)){ $category_ids = array(); } foreach($category_ids as $category_id){ $category_id = intval($category_id); if($category_id <= 0){ continue; } $category_ids_clean[] = $category_id; } /* Get categories from product and load into array */ // Load default page handles and page configurations if ($params && $params->getBeforeHandles()) { foreach ($params->getBeforeHandles() as $handle) { /* Add support for category_{id} layout */ foreach($category_ids_clean as $category_id){ $resultPage->addPageLayoutHandles(['category' => $category_id], $handle, false); } /* Add support for category_{id} layout */ $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); } } /* Add support for category_{id} layout */ foreach($category_ids_clean as $category_id){ $resultPage->addPageLayoutHandles(['category' => $category_id], null, false); } /* Add support for category_{id} layout */ $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false); $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); if ($params && $params->getAfterHandles()) { foreach ($params->getAfterHandles() as $handle) { /* Add support for category_{id} layout */ foreach($category_ids_clean as $category_id){ $resultPage->addPageLayoutHandles(['category' => $category_id], $handle, false); } /* Add support for category_{id} layout */ $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); } } // Apply custom layout update once layout is loaded $update = $resultPage->getLayout()->getUpdate(); $layoutUpdates = $settings->getLayoutUpdates(); if ($layoutUpdates) { if (is_array($layoutUpdates)) { foreach ($layoutUpdates as $layoutUpdate) { $update->addUpdate($layoutUpdate); } } } $currentCategory = $this->_coreRegistry->registry('current_category'); $controllerClass = $this->_request->getFullActionName(); if ($controllerClass != 'catalog-product-view') { $pageConfig->addBodyClass('catalog-product-view'); } $pageConfig->addBodyClass('product-' . $product->getUrlKey()); if ($currentCategory instanceof \Magento\Catalog\Model\Category) { $pageConfig->addBodyClass('categorypath-' . $this->categoryUrlPathGenerator->getUrlPath($currentCategory)) ->addBodyClass('category-' . $currentCategory->getUrlKey()); } return $this; } }
This is our helper file that we will use to overload the ‘initProductLayout’ function in the default ‘vendor/magento/module-catalog/Helper/Product/View.php’ file.
It adds support for the new layout handle, catalog_product_view_category_{CATEGORY_ID}, by getting a list of the products categories, then adding a layout handle for each one. The changes are commented by ‘/* Get categories from product and load into array */’ and ‘/* Add support for category_{id} layout */’. An example of one of these updates is here:
/* Add support for category_{id} layout */ foreach($category_ids_clean as $category_id){ $resultPage->addPageLayoutHandles(['category' => $category_id], $handle, false); } /* Add support for category_{id} layout */
This works through the $category_ids_clean array and adds a layout update for each one.
Next, we need to tell magento to use this class, which we can do by creating a di.xml file here: app/code/Company/Plugin/etc/di.xml
As with the previous step, make sure to change the namespace(‘Company\Plugin’) accordingly to match your plugin.
<?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\Helper\Product\View" type="Company\Plugin\Helper\Product\View" /> </config>
From the command line, clear your site’s cache, so that the new config files are parsed:
/bin/php /home/web/www/bin/magento cache:clean
If all goes well, you should now be able to use an xml layout file to change products based on their category.
An example of how this might look is like this: app/code/Company/Plugin/view/frontend/layout/catalog_product_view_category_1.xml
Where, ‘1’ is the numeric category ID found in the admin categories section.
The site I tested this on has only a few categories for each product, with little nesting. If you make heavy use of categories, you may want to consider only doing the main top level category.
If you need help, feel free to post a comment or contact me. I provide Magento plugin and theme development for both Magento 1 and Magento 2, so if you aren’t a developer, let me know and we can discuss how to get this added to your site.
Add a Comment