Different product layout based on product custom attribute in Magento 2

Posted on 2 June, 2018

All Magento pages are rendered from specific layouts among which product page has catalog_product_view, category page has catalog_category_view, homepage has cms_index_index layout.

As the title indicates,  we can implement different product layouts based on its custom attributes. Now, to understand it more precisely, let's take an example of how to override the product page layout.

Let's say our client has various types of products and along with that, he has some special products too. So now the client wants to present that products into some different layout. So to fulfill his requirements, we need to override layout for some specific product. Let's see the process in detail.

To override layout for just one specific product:

Create layout file in your current theme catalog_product_view_id_123.xml

We hereby prefer to use product ID. But in-case if that is not reliable you can use SKU  for targeting product. In that case your layout file's  name will be in the below mention form:
catalog_product_view_sku_productsku.xml

To override layout for specific product type:

As we know that Magento provides various product types like Simple, Configurable, Group, Bundle, Virtual and Downloadable. Now, if you want to change product layout based on these types, then you need to override layout file as mentioned below.

catalog_product_view_type_simple.xml
catalog_product_view_type_configurable.xml
catalog_product_view_type_grouped.xml
catalog_product_view_type_bundle.xml
catalog_product_view_type_virtual.xml
catalog_product_view_type_downloadable.xml

Now suppose the client has some specific product collections and wants to change those layouts. Here the scenario is that it is absolutely not possible to change layouts by specific product type and id. So now what would be the solution?

For that, we have two specified solutions:

Here I have created customlayout  product attribute  it is not mandatory to use same attribute name , you can use any other attribute of your choice.

Solution 1 :

Step 1: Create product attribute 'customlayout'.

  • Create custom product-attribute for custom layout. Here I have just created attribute 'customlayout' with type boolean(yes/no).

  • Assign this attribute in an appropriate attribute-set.

  • Now, to apply different layouts you need to set 'customlayout' option as 'Yes'.

Step 2: Override Helper function initProductLayout().

Create/Override above helper function in your custom module. For that you need to make changes in di.xml file (which is located in your etc folder).

app/code/YourCompany/YourModule/etc/di.xml

1
2
3
4
<?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="YourCompany\YourModule\Helper\Product\View" />
</config>

YourCompany\YourModule\Helper\Product\view.php file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
namespace OurCompany\YourModule\Helper\Product;
use Magento\Framework\View\Result\Page as ResultPage;
/**
 * Catalog category helper
 */
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());
        if ($params && $params->getBeforeHandles()) {
            foreach ($params->getBeforeHandles() as $handle) {
                $resultPage->addPageLayoutHandles(['customlayout' => $product->getCustomlayout(), 'id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
                $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false);
            }
        }
        $resultPage->addPageLayoutHandles(['customlayout' => $product->getCustomlayout(), 'id' => $product->getId(), 'sku' => $urlSafeSku]);
        $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false);
        if ($params && $params->getAfterHandles()) {
            foreach ($params->getAfterHandles() as $handle) {
                $resultPage->addPageLayoutHandles(['customlayout' => $product->getCustomlayout(), 'id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
                $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false);
            }
        }
        // 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;
    }
}

Here I have created the custom attribute for custom layout handle. Now you need to create custom layout file to use this attribute.
Let's see how to create custom handle file.

Step 3: Create layout file for custom layout.

Create layout file in your custom theme/ custom module. [Theme]/frontend/Magento_catalog/layout/catalog_product_view_customlayout_1.xml
[Vendor]/[Module]/view/frontend/layout/catalog_product_view_customlayout_1.xml

You can make your custom changes in it and they will be applied to all the products where 'customlayout' attribute value is set to 'Yes'.

Note: If you are using different attribute then your layout would be catalog_product_view_attributename_attributevalue.xml

Solution 2:

Step:1 Follow steps from solution -1.

Here what you need to do is add layout via event layout_load_before, and this event will be used to add dynamic layout.

Step2: Create observer LayoutLoadBefore()

Createevent.xml in your module
[Vendor]/[Module]/etc/frontend/events.xml

1
2
3
4
5
6
7
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <!-- for diffrentiate layout.xml on product basis -->
    <event name="layout_load_before">
        <observer name="load_custom_handler" instance="[Vendor]\[Module]\Observer\LayoutLoadBefore" />
    </event>
</config>

Write the below code in your [Vendor]\[Module]\Observer\LayoutLoadBefore.php file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
namespace [Vendor]\[Module]\Observer;
class LayoutLoadBefore implements \Magento\Framework\Event\ObserverInterface
{
    /**
     * @var \Magento\Framework\Registry
     */
    protected $_registry;
    public function __construct(
       \Magento\Framework\Registry $registry,
    )
    {
        $this->_registry = $registry;
    }
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $product = $this->_registry->registry('current_product');
        if (!$product){
          return $this;
        }
        if($product->getCustomlayout() ==1){ // your condition
           $layout = $observer->getLayout();
           $layout->getUpdate()->addHandle(‘catalog_product_view_customlayout_1’);
        }
        return $this;
    }
}

Step 3: Create layout file for custom layout.

Create layout file in your custom theme/ custom module. [Theme]/frontend/Magento_catalog/layout/catalog_product_view_customlayout_1.xml
[Vendor]/[Module]/view/frontend/layout/catalog_product_view_customlayout_1.xml and write your code.

Note: If you are using different attribute then your layout would be catalog_product_view_attributename_attributevalue.xml. replace  your attribute name and value  with 'customlayout' and its value in above code.

1
2
3
4
5
6
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        ...
        ...
    </body>
</page>

Both solutions are valid to change product layout with specific product conditions. You can use either observer or can add layout handles attributes.


Nimita Gajera , eCommerce Project Manager

Magento Technical Notes

About Emipro

Being an emerging leader in IT market since 2011, Emipro Technologies Pvt. Ltd. has been providing a wide range of business solutions in Odoo & Magento. We are pleased to have a large pool of contented customers with our meticulous work in the domain of ERP & e-Commerce. Our customers are companies of all sizes ranging from startups to large enterprises who realize that they need a professional internet solution to generate revenue streams, establish proper communication channels, to achieve desired goals and streamline business operations. [....] Read More

Our writings seems informative ?

Subscribe for our Magento Technical Notes and get more amazing stuff directly to your inbox!

Post Your Review

X

Your Review has been posted

0 Comment(s)