Digging Deeper Into Magento’s Layout XML – Part 2
Digging Deeper Into Magento’s Layout XML – Part 2
In the first part of this series, we discussed the basics of Layout XML elements and gave an overview of the rendering process.
In part 2, we are going to discuss both in some more detail and present some techniques to perform more advanced layout actions out of the box.
First, let’s explain a few terms that are frequently used in this article:
MVC Action: MVC action is nothing but a method defined in a controller class and mapped with the requested URL. For example: when a category page is requested, the action method called is Mage_Catalog_CategoryController::viewAction()
.
MVC action methods perform the backend processing for the current requested page like retrieving and storing data from/to the database.
Layout Instance: Layout Instance is responsible for the page to be rendered. Before the page is rendered, Magento always creates a Layout Instance.
Layout Update Instance: While the Layout Instance is responsible for page rendering, the Layout Update Instance is responsible for all the updates to be added in the page layout once the layout instance has been created. This instance contains the data of all layout handles added to the current page.
Output Blocks: Output blocks are like normal blocks but they are directly involved with the response. When the layout is rendered, only the output blocks are rendered automatically. So it is output block’s responsibility to render its child blocks by calling a method like $this->getChildHtml()
. For example, in the default theme, only the root block is an output block so at response time, only the root block is actually rendered but its phtml template calls methods like $this->getChildHtml('head')
which renders its child blocks like the head block. So, output blocks are rendered automatically while other blocks are rendered if they are invoked by their parent blocks.
Digging Deeper Into The Rendering Process
In Magento, the rendering process is divided in two steps:
- Initializing page layout
- Sending response with the output from blocks.
Layout Initialization
The layout is normally initialized in an MVC Action method by calling $this->loadLayout()
. The loadLayout()
method accepts three optional arguments:
$handles:
This argument is either a string or array of strings. Each string is a name of layout handle. If this argument is passed, the specified handle(s) are added to the Layout Update Instance. If the argument is not passed or passed as null
, the handle default
is added automatically. If the argument is passed as a blank string or false
, then the default handle is not added.
$generateBlocks:
This is a Boolean argument with default value of true
. If it is set to false
, the blocks defined in the layout XML are not instantiated.
$generateXml:
This is also a Boolean argument with default value of true
. If it is set to false
, Layout updates are loaded but not applied to the page. Also the argument $generateBlocks
would have no effect in this case and no layout blocks are instantiated.
The layout initialization happens as follows:
- Instances of the Layout and Layout Update are created.
-
Layout handles are added according to the
$handles
argument if passed. -
Store layout handle
STORE_[store_code]
is added to the Layout Update Instance. For example, if code of current store is en, then layout handleSTORE_en
is added. -
Theme layout handle
THEME_[area]_[package]_[layout_theme]
is added to the Layout Update Instance. For example, if the page rendered is for the frontend area, the current theme package name ismagebase
and theme name for layout ismodern
, then the layout handleTHEME_frontend_magebase_modern
is added. -
Action layout handle is added to the Layout Update Instance. For example, if the page rendered is a category detail page, then Magento is executing
catalog
module’scategory
controller’s view action. So it will add an action handlecatalog_category_view
. - All Layout XML files defined for all active modules are loaded
-
If a layout file named
local.xml
exists in the current theme’s layout folder, it is loaded last - Layout updates of all added layout handles from the loaded layout XML are merged
- Layout updates of all added layout handles from the database are merged
-
If the
$generateXML
argument ofloadLayout()
method is passed asfalse
, the initialization is finished. -
The layout update data is refined by removing all blocks and block references defined with the
remove
tag. (As discussed in Part 1) -
If
$generateBlocks
argument is passed asfalse
, the initialization is finished. - The instances of block classes are created according to the block definitions in the Layout XML
- The methods are called with specified arguments on the blocks where action tags are defined. (As discussed in Part 1)
As a result of the rendering process, we can establish the different scope of the layout handles:
- default layout update has global scope and applied for all pages.
- Store layout handle is applied according to the current store.
-
Theme layout handle is according to the current theme.
At first, it may seem that this layout handle is not so useful because we define the layout XML within the theme. So if the theme is changed, the layout XML would also be changed. But layout XML is not only specified within a theme’s layout folder. It is also used in layout updates defined in the admin panel for CMS pages, products, categories etc., usually in the Layout Update XML field. The consequence of this is that we can target different theme layout updates from the same Layout Update XML field. This comes in handy, for example, if we enable the mobile theme detection and want to make specific layout changes on the home page depending on whether the theme rendered is the mobile or the main site theme. - Action layout handle is applied according to the current MVC Action being executed.
Output Block Rendering
After the layout is initialized, the output is normally returned by calling the $this->renderLayout()
method. This method accepts one optional argument: $output
. When this argument is passed with an existing block name, that block is considered as an output block and rendered automatically when the layout is rendered. Magento also considers the block as an output block when its layout XML definition contains the output
attribute. For example, in the default theme, the root block is defined as:
1
|
< block type = "page/html" name = "root" output = "toHtml" template = "page/3columns.phtml" > |
Here, root
block is considered as an output block as its definition specifies the output
attribute. The value of the output
attribute defines the method called to render the block. Here root
block’s toHtml
method is called to render it.
A layout should have at least one output block. Normally, the root
block is the only output block in a layout but there can be multiple output blocks for a single page. In that case, the output of each output block is merged and returned in the response. Block Name and Alias
Block name and alias differ from each other in terms of the scope. The block name is unique within the whole page that is being rendered, while the alias is unique only within the parent block.
1
|
< block type = "catalog/product_view" name = "product.info.addtocart" as = "addtocart" template = "catalog/product/view/addtocart.phtml" /> |
So when a block is referenced within any block defined in the layout or any layout handle, its name is used in that reference. But when referencing the block within its parent block, the block’s alias can also be used. The block name is normally a longer, descriptive name while the alias is a short name.
In the example above, the ‘Add to cart’ block has the name product.info.addtocart
and the alias is addtocart
.
Block ordering with after
and before
attributes
As discussed in Part I, the attributes before
and after
can be used in the block definition to define its position in the sequence of created blocks. These attributes can have values that specify either the name or alias of a block.
1
|
< block type = "catalog/product_compare_sidebar" before = "cart_sidebar" name = "catalog.compare.sidebar" template = "catalog/product/compare/sidebar.phtml" /> |
As in the example above, product compare sidebar block defines the before
attribute with a value of cart_sidebar
which refers to the cart sidebar block. This means that the product compare sidebar block will be positioned before the cart sidebar block.
When the layout is initialized, the blocks are automatically sorted according to the before
and after
attributes.
We can also specify a value of ‘-‘ for the before
or after
attribute in which case the block will be moved to the first or last position in the block order within its parent block.
Non-output block rendering
As explained earlier, the non-output blocks are child blocks of output blocks and are normally rendered with the getChildHtml()
method. There are also two other methods: getChildChildHtml()
and getBlockHtml()
also used to render non-output blocks.
getChildHtml()
This method renders a child block according to the block name or alias supplied in the argument.
The first argument is the name or alias of a child block. If supplied, it returns the output of that child block. If this argument is not supplied or passed as a blank string, it renders all the child blocks specified in the layout.
The second argument $useCache
is a Boolean which is by default true
. If it is true
, the block is cached if the block cache is enabled under the Caching settings in the admin panel. If it is false
, the block is not cached even if block cache is enabled.
The third argument $sorted
is also a Boolean which is by default false
. If it is true
, the child blocks are rendered according to the sorting order defined by before and after attributes.
getChildChildHtml()
This method is slightly different from the getChildHtml()
method. getChildHtml()
renders the specified child block while getChildChildHtml()
renders all child blocks of the specified block.
For example, Block A has a child Block B and Block B has child Blocks C and D. Now inside Block A, we call:
1
|
echo $this ->getChildHtml( 'B' ); |
This renders the output of Block B. However, if we call:
1
|
echo $this ->getChildChildHtml( 'B' ); |
This renders all child blocks of B which means, Block C and D are rendered.
The method accepts four arguments: $name
, $childName
, $useCache
and $sorted
. The last two are the same as in getChildHtml()
method.
$name
argument is mandatory here which defines the parent block of which child block(s) are rendered.
$childName
argument is optional. If it is passed, only that child block of the parent block is rendered otherwise all child blocks are rendered.
getBlockHtml()
This method is used to render any block defined anywhere in the layout. Unlike, getChildHtml()
, it doesn’t need to be called in the parent block only.
This method accepts only one mandatory argument $name
. This should be the fully qualified name of the block and not an alias.
Product Type Handles
There are multiple product types in Magento, i.e. Simple Products, Configurable Products, Bundle Products, Group Products etc. Normally, there are some common blocks that remain the same for any product type. For example, product image, product name, description, up-sell products block etc. But each product type may have its specific features that should be shown on the product page. For example, configurable products should display selection boxes of each configurable option; bundle products should display its bundled items etc.
As discussed previously, layout update handles define the changes to the page layout out of the box. Magento utilizes this flexibility for handling product type specific changes on the product page.
Like all other normal pages, when a product page is requested it adds a layout update handle according to the module, controller and action which is catalog_product_view
. Additionally, it automatically detects the type of product and adds a product type specific layout update handle. The format of the layout update handle name is PRODUCT_TYPE_[product_type_code]
where [product_type_code]
is a system code for the product type. Product type specific blocks or layout updates can be defined under these special purpose layout handles.
The following table shows the layout update handles loaded for built-in product types:
Product type | Layout update handle |
---|---|
Simple | PRODUCT_TYPE_simple |
Configurable | PRODUCT_TYPE_configurable |
Grouped | PRODUCT_TYPE_grouped |
Virtual | PRODUCT_TYPE_virtual |
Downloadable | PRODUCT_TYPE_downloadable |
Bundle | PRODUCT_TYPE_bundle |
To see the product type handles in action, you can study the catalog.xml
file. These layout update handles are defined towards the end of the file.
Creating Our Own Layout Handles
In terms of reusability, we can compare layout handles in Magento with user defined functions in normal code. A user defined function may contain a snippet of code to be used in many places, so that we just need to call the function where needed instead of repeating the same code everywhere.
Layout handles can be used in a similar way. In a page, there may be some specific layout changes which are repeated for other pages, for example, adding a block in the right sidebar, removing a block from the left sidebar, defining the root
template to use 3 columns, 1 column, 2 columns with left sidebar, etc… We can create a layout handle which defines these repeating changes in one place and then we can just specify the layout update handle for any other page where we need this specific layout change.
Let’s take a simple example. Let’s say that we need the following layout changes in more than one page:
- Remove the cart sidebar block from the right sidebar
- Insert a CMS block with identifier foo in the left sidebar
- Use the 3 column layout for the page
We have to define a unique name for the handle that doesn’t conflict with the name any existing layout handles. In our example, we will give the layout handle the name foo_bar_handle
.
Here is the layout XML code to define this handle:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< foo_bar_handle > < reference name = "right" > < remove name = "cart_sidebar" /> </ reference > < reference name = "left" > < block type = "cms/block" name = "foo" > < action method = "setBlockId" >< identifier >foo</ identifier ></ action > </ block > </ reference > < reference name = "root" > < action method = "setTemplate" >< template >page/3columns.phtml</ template ></ action > </ reference > </ foo_bar_handle > |
Now, let’s say, we need the above changes on all the product pages. So, we can call our layout handle on the product page like this:
1
2
3
|
< catalog_product_view > < update handle = "foo_bar_handle" /> </ catalog_product_view > |
This will update the product page with the layout changes specified in our custom layout update handle.
To keep all our layout updates in one central place, we recommend placing them in the local.xml
file.
local.xml
As mentioned above, Magento by default reads this file if it exists at the very end of the layout instantiation. This is a very important detail since it allows us to use this file to affect the default layout of a theme without having to change the theme’s layout XML files. That was we also keep all our custom layout changes in one place for easy reference.
We wanted to expand on the local.xml
file and usage but realized that there are already several articles and tutorials about this so we decided to point you to those in our resources section below.
Summary
The Architecture of Magento’s Layout XML looks complex in its internal design but it is relatively simple in usage. However, being complex internally, it also provides some advanced features to achieve maximum flexibility in design. The next article in this series will delve further into some of these techniques which can be useful in advanced theme development.