As discussed in previous articles about Magento’s Layout XML, Layout Handles in Magento’s layout XML are XML nodes which contain the definition of updates in page layouts. There are some built in Layout Handles which are called implicitly in Magento. For example, default handle is called for every page, PRODUCT_TYPE_configurable is called for the configurable product detail page, customer_logged_in is called if a customer is logged in, etc.

But what if we want to add a custom set of layout updates for our own condition? For example, if we wanted our product pages to be different depending on its attribute set. Magento already provides layout handles for each product type. It also provides a layout handle PRODUCT_[product_id] where [product_id] is replaced by the product’s ID in the database. Using this layout handle, we can easily define a layout update for a specific product. But in our case, we want to define a layout handle for all products with a particular attribute set. In this case, it is not practical to define PRODUCT_[product_id] handles for each product in that attribute set, because new products added in that attribute set will not have the same design update and we would have to keep adding additional handles.

So ideally, we need a way to define a layout handle for attribute sets. Something like the following:

<PRODUCT_ATTRIBUTE_SET_shirts>
    <reference name="product.info">
      <action method="setTemplate"><template>my/custom/product/view.phtml</template></action>
    </reference>
</PRODUCT_ATTRIBUTE_SET_shirts>

Fortunately, Magento’s layout engine is flexible enough to provide a way to define our own layout handles and call them explicitly through custom extensions. In the example of the case we discussed above, we will see how to create our own layout handles.

As described in our previous tutorials on Layout XML, after the layout XML is loaded, Magento applies the default handle, store handle, theme handle and then action handle. At the very end, layout updates from the database are applied.

For the product page, two additional layout handles: PRODUCT_TYPE_[type_id] and PRODUCT_[product_id] are applied after the action handle and before the layout updates from the database. In our case, we need to create and apply one more additional handle so that we can target our attribute set, namely: PRODUCT_ATTRIBUTE_SET_[attribute_set].

To accomplish this, we will use the event observer approach. This will keep our customization clean and not interfere with other extension.

Magento fires an event controller_action_layout_load_before before applying layout updates from the database. We can add a method observing this event in our extension which will add our attribute specific layout handle.

So, we have to follow the steps described below:

  1. Create an extension which observes the event: controller_action_layout_load_before and calls a method to add our custom layout handle.
  2. In the observer method, we will check if it is a product page and if not stop the execution or go to step 3.
  3. We’ll fetch the current product’s attribute set name.
  4. For the attribute set name, Magento allows characters like space, comma, period, etc. which are not valid as XML tag names. Since the layout handle is also an XML tag, we need to convert it to a XML tag compatible name. In short we need to remove all invalid characters from the attribute set name and replace them with underscores (‘_’).
  5. Finally, we add the new layout handle PRODUCT_ATTRIBUTE_SET_[attribute_set_nicename] where [attribute_set_nicename] is the attribute set name which is converted to a XML tag compatible format.

We will give our extension the name: Magebase_AttributeSetHandle. First, we will create app/etc/modules/Magebase_AttributeSetHandle.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Magebase_AttributeSetHandle>
            <active>true</active>
            <codePool>local</codePool>
        </Magebase_AttributeSetHandle>
    </modules>
</config>

And, app/code/local/Magebase/AttributeSetHandle/etc/config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Magebase_AttributeSetHandle>
            <version>0.1.0</version>
        </Magebase_AttributeSetHandle>
    </modules>
    <global>
        <events>
            <controller_action_layout_load_before>
                <observers>
                    <attributesethandle>
                        <class>Magebase_AttributeSetHandle_Model_Observer</class>
                        <method>addAttributeSetHandle</method>
                    </attributesethandle>
                </observers>
            </controller_action_layout_load_before>
        </events>
    </global>
</config>

Here, in config.xml we declare an observer method for the controller_action_layout_load_before event as Magebase_AttributeSetHandle_Model_Observer::addAttributeSetHandle().

Now, we will create a class: Magebase_AttributeSetHandle_Model_Observer and a method: addAttributeSetHandle() in app/code/local/Magebse/AttributeSetHandle/Model/Observer.php.

<?php
class Magebase_AttributeSetHandle_Model_Observer
{
    /**
     * Converts attribute set name of current product to nice name ([a-z0-9_]+).
     * Adds layout handle PRODUCT_ATTRIBUTE_SET_<attribute_set_nicename> after
     * PRODUCT_TYPE_<product_type_id> handle
     *
     * Event: controller_action_layout_load_before
     *
     * @param Varien_Event_Observer $observer
     */
    public function addAttributeSetHandle(Varien_Event_Observer $observer)
    {
        $product = Mage::registry('current_product');

        /**
         * Return if it is not product page
         */
        if (!($product instanceof Mage_Catalog_Model_Product)) {
            return;
        }

        $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($product->getAttributeSetId());
        /**
         * Convert attribute set name to alphanumeric + underscore string
         */
        $niceName = str_replace('-', '_', $product->formatUrlKey($attributeSet->getAttributeSetName()));

        /* @var $update Mage_Core_Model_Layout_Update */
        $update = $observer->getEvent()->getLayout()->getUpdate();
        $update->addHandle('PRODUCT_ATTRIBUTE_SET_' . $niceName);
    }
}

Here, to convert the attribute set name to a XML tag compatible name, we have called:

$niceName = str_replace('-', '_', $product->formatUrlKey($attributeSet->getAttributeSetName()));

This is because, formatUrlKey() in the product class, replaces all non-alphanumeric characters to ‘-’. But ‘-’ is not valid in XML tag name, so we are replacing it with ‘_’.

After that we add our handle for the attribute set. However, there is one shortfall to our observer approach.

On the product page, after the action handle is applied, the product type and product id handles are applied immediately before the controller_action_layout_load_before event is fired. This means that our attribute set handle is applied after the product id handle, which is not ideal.

The reason is that if we use both the product id handle and product attribute set handle in our custom layout modifications, then the attribute set handle will override the product id handle updates. This is not a practical because the product id handle should always be applied after the attribute set handle since it is specific to a product. Our handle hierarchy should be:

PRODUCT_TYPE_[type] => PRODUCT_ATTRIBUTE_SET_[attribute_set_nicename] => PRODUCT_ID_[id]

Unfortunately, there is no event fired between the handles applied for product type and product id so, we have to rearrange the layout handles in our observer method. So our method would be as below:

<?php
class Magebase_AttributeSetHandle_Model_Observer
{
    /**
     * Converts attribute set name of current product to nice name ([a-z0-9_]+).
     * Adds layout handle PRODUCT_ATTRIBUTE_SET_<attribute_set_nicename> after
     * PRODUCT_TYPE_<product_type_id> handle
     *
     * Event: controller_action_layout_load_before
     *
     * @param Varien_Event_Observer $observer
     */
    public function addAttributeSetHandle(Varien_Event_Observer $observer)
    {
        $product = Mage::registry('current_product');

        /**
         * Return if it is not product page
         */
        if (!($product instanceof Mage_Catalog_Model_Product)) {
            return;
        }

        $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($product->getAttributeSetId());
        /**
         * Convert attribute set name to alphanumeric + underscore string
         */
        $niceName = str_replace('-', '_', $product->formatUrlKey($attributeSet->getAttributeSetName()));

        /* @var $update Mage_Core_Model_Layout_Update */
        $update = $observer->getEvent()->getLayout()->getUpdate();
        $handles = $update->getHandles(); // Store all handles in a variable
        $update->resetHandles(); // Remove all handles

        /**
         * Rearrange layout handles to ensure PRODUCT_<product_id>
         * handle is added last
         */
        foreach ($handles as $handle) {
            $update->addHandle($handle);
            if ($handle == 'PRODUCT_TYPE_' . $product->getTypeId()) {
                $update->addHandle('PRODUCT_ATTRIBUTE_SET_' . $niceName);
            }
        }
    }
}

Here, we have stored all added layout handles in an array variable, then reset the updates. So now layout update instance has no layout handle. Then we iterate over all the handles we have stored and add them one by one again except when we find the product type handle, we add our attribute set handle immediately after that. This way, the attribute set handle is added exactly where we want it.

Conclusion

In this tutorial, we saw how we can add custom layout handles if we need to by observing the controller_action_layout_load_before event. In our example, we added a handle that allows us to create specific layouts for product pages that show products with a specific attribute set. Similarly, we could implement a custom layout handle based on some other attribute value or condition.

Let us know if you discover some more interesting scenarios with your own custom layout handles.

Originally published on magebase.com. Copyright © 2011 Magebase - All Rights Reserved.


Back Older article Newer article

New theme released

Responsive Magento Theme - Gala Marcos

A truly impressive Magento template for fashion store from Galathemes, Gala Marcos. It amazes visitors by modern and high-fashion look, and also, neat design.

Read more

Our services

Installation

Magento Custom Development

Magento is the most powerful eCommerce system offering rich customization possibilities by extensions and modules.

We offer custom extension development performed by our full-time Magento experts to ensure the custom extension developed follow Magento code standard, optimized and pass our quality tests.

Read more

design

Magento Custom Design

Design and development a custom Magento template for your Magento store. Our designers and developers are specialists in Magento Commerce and have strong experience in Magento projects.

We provide all design in PSD files, template package and sample data. We also help you install the theme on your store if required. We start your project instantly and with highest priority.

Read more

Magento Template Conversion

PSD to Magento Theme Conversion

PSD to Magento Theme Conversion is a leading strength of us. We have an intelligent process and experienced staff, so you will save much time.

We easily convert a store designs in PSD format into a fully functional Magento commerce template. Quick and convenient for you to create an online store based on Magento is through "PSD to Magento Theme Conversion" service. We bring the flexibility, user friendly modules, and the extensions to improve the functionality of Magento.

Read more

Development

Magento Site Development

We update our Magento knowledge everyday. Having an excellent knowledge on Magento design, Magento programming and server optimization, we guarantee your project get done perfectly.

We apply the philosophy of agile project management to ensure your project always performs on the right way, you'll get updates frequently, any changes of scope of project can be informed early to minimize risks, time and cost.

Read more

Optimization

Magento Server Optimization

We realy provide the best service for you. Among them are optimized for Magento server is very important. Your ecommerce shop will flexibility and agility absolute. Connecting with customers, processing speed, the gentle query and sensitive to the search engine is very easy

Read more