Magento Design and Layout Initialization

In this blog post we will see in detail how magento initializes theme, layout files, how the fallback method for theme is setup, how layout is compiled, how html output is generated and other similar things.

Package and Layout Initialization

The design package initialization happens when we call the function $this->loadLayout() in our controller. The function “loadLayout()” is defined in “Mage_Core_Controller_Varien_Action” class.
Lets look at step by step how the code flows in the loadLayout() function.

Lets see the important lines below

$this->getLayout()->getUpdate()->addHandle($handles ? $handles : 'default');

“$this->getLayout()” creates an instance of class “Mage_Core_Model_Layout”
and “$this->getLayout()->getUpdate()” creates an instance of “Mage_Core_Model_Layout_Update”
The “addHandle()” function adds the >default< handle to the layout_update object. This object basically maintains an array of all handles added to it.
Next we have

$this->addActionLayoutHandles();

The “addActionLayoutHandles()” adds three handles as shown below

        $update->addHandle('STORE_'.Mage::app()->getStore()->getCode());

        // load theme handle
        $package = Mage::getSingleton('core/design_package');
        $update->addHandle(
            'THEME_'.$package->getArea().'_'.$package->getPackageName().'_'.$package->getTheme('layout')
        );

        // load action handle
        $update->addHandle(strtolower($this->getFullActionName()));

basically “STORE_default”, “THEME_frontend_{{package}}_{{layout}}” and “{{module}}_{{controller}}_{{action}}” handles are added. (replace {{}} with actual values)

Next we have the function

$this->loadLayoutUpdates();

This function basically reads all layout xml files, loads its configuration and then based on the handles added creates config object. We will look into this function in detail later.

$this->generateLayoutXml();

This function mainly removes any blocks which we have specified in the >remove< tag in layout xml files.

$this->generateLayoutBlocks();

This function creates an instance of all block files and sets block hierarchy (i.e parent/child) blocks. This function also reads >reference< tag in layout files and creates reference blocks as well.

Getting the Package Name

Above a function was used getPackageName(), this function is defined in class “Mage_Core_Model_Package”. This function return the package name if defined or else calls the function setPackageName() to find the default package name.

     public function setPackageName($name = '') {
        if (empty($name)) {
            // see, if exceptions for user-agents defined in config
            $customPackage = $this->_checkUserAgentAgainstRegexps('design/package/ua_regexp');
            if ($customPackage) {
                $this->_name = $customPackage;
            } else {
                $this->_name = Mage::getStoreConfig('design/package/name', $this->getStore());
            }
        } else {
            $this->_name = $name;
        }
        // make sure not to crash, if wrong package specified
        if (!$this->designPackageExists($this->_name, $this->getArea())) {
            $this->_name = self::DEFAULT_PACKAGE;
        }
        return $this;
    }

So as you can see the function first checks the “UserAgent Exception list” which we can define while setting up theme in admin. If no exception is found it simply reads the package name from “core_config_data” table.

Initializing Layout XML Files

Above we saw the function “loadLayoutUpdates”, this function calls another function “load()” in “Mage_Core_Model_Layout_Update” class.
This function loads xml for handles from layout xml files.
There is function fetchFileLayoutUpdates() which finds all layout files from themes.

fetchFileLayoutUpdates()
This is the main code written inside the function

           $this->_packageLayout = $this->getFileLayoutUpdatesXml(
                $design->getArea(),
                $design->getPackageName(),
                $design->getTheme('layout'),
                $storeId
            );

in my case i have selected the “rwd” theme in magento so
$design->getArea() => ‘frontend’
$design->getPackageName() => ‘rwd’
$design->getTheme(‘layout’) => ‘default’

The function getFileLayoutUpdatesXml reads the configuration “frontend/layout/updates” from all config.xml file. This is where we specify the name of layout xml for each module.

        $updatesRoot = Mage::app()->getConfig()->getNode($area.'/layout/updates');
        Mage::dispatchEvent('core_layout_update_updates_get_after', array('updates' => $updatesRoot));
        $updates = $updatesRoot->asArray();
        $themeUpdates = Mage::getSingleton('core/design_config')->getNode("$area/$package/$theme/layout/updates");
        if ($themeUpdates && is_array($themeUpdates->asArray())) {
            //array_values() to ensure that theme-specific layouts don't override, but add to module layouts
            $updates = array_merge($updates, array_values($themeUpdates->asArray()));
        }
        $updateFiles[] = 'local.xml';

So above gets the filenames of theme layout files from modules and also loads an additional local.xml file.

TIP: local.xml is located in layout folder inside theme directory and this can be used to theme specific layouts
Next we have

          foreach ($updateFiles as $file) {
            $filename = $design->getLayoutFilename($file, array(
                '_area'    => $area,
                '_package' => $package,
                '_theme'   => $theme
            ));
            if (!is_readable($filename)) {
                continue;
            }
            $fileStr = file_get_contents($filename);
            $fileStr = str_replace($this->_subst['from'], $this->_subst['to'], $fileStr);
            $fileXml = simplexml_load_string($fileStr, $elementClass);
            if (!$fileXml instanceof SimpleXMLElement) {
                continue;
            }
            $layoutStr .= $fileXml->innerXml();
        }
        $layoutXml = simplexml_load_string('<layouts>'.$layoutStr.'</layouts>', $elementClass);

Here for each layout theme file, the function getLayoutFilename is called to get the full path. Inside the “getLayoutFilename” function all theme fallback operations happen so we will see this next.

Theme Fallback

As we know magento implements a fallback method to default/base theme if a certain file is not found in the main theme. Lets see how this is implemented in code.
Above we saw the function “getLayoutFilename()”, this function is defined in the class “Mage_Core_Model_Package”.
Similarly we have function like “getTemplateFilename()”, “getLocaleFileName()”, “getSkinUrl()” which are used to find file name with fallback scheme implemented. All the above function call “getFilename()”

        $result = $this->_fallback(
                $file, $params, $this->_fallback->getFallbackScheme(
                        $params['_area'], $params['_package'], $params['_theme']
                )
        );

“$this->_fallback” is an instance of class “Mage_Core_Model_Design_Fallback”
We we check the function “getFallbackScheme()” this is the main code written

            if ($this->_isInheritanceDefined($area, $package, $theme)) {
                $scheme = $this->_getFallbackScheme($area, $package, $theme);
            } else {
                $scheme = $this->_getLegacyFallbackScheme();
            }

The function “_isInheritanceDefined()” checks the xpath “$area . ‘/’ . $package . ‘/’ . $theme . ‘/parent'” in your
package/theme/etc/theme.xml file
e.g the “rwd/default” theme’s theme.xml contains

<theme>
    <parent />
</theme>

So this has an empty parent, but still the “_isInheritanceDefined” return true. In this case the fallback is directly “base”, it wont check the “default” theme. But in the above theme.xml we can specify a fallback theme if required like

<theme>
    <parent>default/mordern</parent>
</theme>

So this would create a proper chain for fallback.