Mini Tutorial: Dynamically adding and changing blocks in Magento

April 1, 2013 by Corey

Sometimes when we’re developing specific functionality for a Magento website, we’ll need to shift around, modify, add, or remove blocks within the layout. Sometimes this is a skinning issue and can be achieved by moving around the calls to echo $this->getChildHtml(), other times it may require an override within our custom theme’s local.xml file. However, what if we wanted to change the layout or change blocks dependant on some custom factors? There are some built-in methods in Magento which I’ll mention now, however it’s worth looking a little more in-depth and getting our hands dirty.

Page-Specific Handles

Handles are used in Magento as the “parent nodes” in the layout XML for defining which layout we’re using. Example: catalog_product_view is the handle for the product view page in Magento, and cms_index_index is used for the homepage. These handles can extend beyond the basic front_name / controller_name / action_name in that we can target specific categories, products and stores. In certain controllers (category controllers, product controllers) they directly use the addHandle(‘handle_name’) function on an update object. You should be able to get the current update object in any controller, helper, or block if you’ve extended classes correctly via $this->getLayout()->getUpdate(). Below are some layout handles which can be used to target specific cases within your Magento site:

Store-specific handle:
<STORE_#> (where ‘#’ is the store ID – if you only have one store it is most likely ‘1’)

Category-specific handle:
<CATEGORY_#> (where ‘#’ is the ID number of the current category)

Product-specific handle:
<PRODUCT_#> (where ‘#’ is the ID number of the current product)

Product type-specific handle:

<PRODUCT_TYPE_NAME> (where ‘name’ is the product type, example: ‘simple’, ‘grouped’, ‘configurable’, etc.)

Customer logged in or out:

<CUSTOMER_LOGGED_STATUS> (where ‘status’ is either ‘in’ or ‘out’)

If CMS page:

<cms_page> (if we’re currently on a CMS page)

As you can see, adding your own unique handles for specific circumstances can become very handy for others to extend and assist in changing things dynamically on a Magento site. Note that we can add layout specific information in a few places in the backend. Some include but are not limited to the product edit / creation page:

…the category creation / edit page:

…and the CMS creation / edit page:

There are arguments whether one should create their specific custom layout XML within these methods, or the methods specified earlier by placing the updates in your local.xml file. I would argue that the method of using local.xml is beneficial as you have every update in one location, and it is easy to find and modify at a later date. As well, if a product / category gets deleted, it would be difficult to retrieve your specific update information.

Dynamically Render Blocks Based on Specifics

Sometimes using these helpful handles is not enough. There is the rare case that we may need to update specific information about the block before it’s rendered. For example, recently I had to create functionality for including different sizes of advertisements on one of our sites. This was achieved by defining a block in the ad module’s layout XML like this:

In our Ad.php block file, we can add a _beforeToHtml() function definition which will be called before the block is rendered. From here, we can extract the size of the ad block from the alias and then set the appropriate template. Check out the following code:

protected function _beforeToHtml()
    {
        $alias = explode('_', $this->getBlockAlias());
        $size = isset($alias[2]) ? $alias[2] : false;
        if ($size !== false) {
            $helper = Mage::helper('doubleclick');
$file = $helper->getFilePath('template', $size);
            if ($filePath && $helper->doesFileExist('design', $filePath)) {
                $this->setTemplate($filePath);
            }
        }

        return parent::_beforeToHtml();
    }

If everything goes according to plan, and the file exists, we then set the appropriate ad size template on to the block which is targeted by the page. The getFilePath() function in the helper ends up figuring out the current category and creating the path appropriately, and the doesFileExist() function just makes sure that we’re setting a template file which exists to avoid a fatal error.

public function getFilePath($type = 'template', $size = false)
    {
        if ($categoryName = $this->getParentCategoryName()) {
            $categoryName = strtolower(preg_replace('/[^\w]+/', '', $categoryName));
            return 'demac/doubleclick/' . $categoryName . '/' . $size . '.phtml';
        }

        return false;
    }

public function doesFileExist($baseDir = 'design', $file)
    {
        $filename = Mage::getBaseDir($baseDir) . '/' . Mage::getDesign()->getTemplateFilename($file, array('_relative' => true));
        return file_exists($filename);
    }

Note that all functions available to you within the layout XML via are also available to call on any block in our code. For example, if we instantiated a new block to be added / appended to the head, we could execute something similar to this:

$block = $this->getLayout()->createBlock(
                    'doubleclick/example',
                    'doubleclick.example',
                    array('template' => 'demac/doubleclick/example.phtml')
                );
$this->getLayout()->getBlock('head')->append($block);

Enjoy moving around blocks!

About Corey

Certified Magento Developer

Read more posts by Corey