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:
<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.
<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.
<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:
echo $this->getChildHtml('B');
This renders the output of Block B. However, if we call:
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:
<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:
<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.
Resources
- http://classyllama.com/development/magento-development/the-better-way-to-modify-magento-layout/
- http://inchoo.net/ecommerce/magento/using-local-xml-for-overriding-or-updating-xml-structure/
- http://store.alanstorm.com/products/commerce-bug
Originally published on magebase.com. Copyright © 2010 Magebase - All Rights Reserved.
[…] Magneto’s layout rendering process and gives more insight into layout handles and updates. Link – Trackbacks source: Topsy – magento tutorial – Digging Deeper Into Magento’s […]
Thanks for Part 2, it covers some things i’ve not seen mentioned elsewhere. Such as the fact you can use block names in after and before rather than having to depend on the parent.
I’m having troubles with this though. I have a block that displays on the shipment view page in the admin area. That works fine however I cant see to get my block to appear exactly where I want it. I want to display it between the block order_payment and shipment_tracking … so I try passing after=”order_payment”. This just displays my block at the very end of the page, after all other blocks. I also tried before=”shipment_tracking” however this again just displays my block at very start of page before all other blocks.
So it seems I can get my block to display before and after all other blocks, but cannot nest it inbetween blocks.
Is this something you have seen before or have an idea on?
cheers,
happy
@Happy
Trying to get a specific order for your blocks in the layout xml can sometimes be a challenge and requires some ‘voodoo’. You will need to check all the before and after attribute values for the blocks you are trying to nest between, sometimes they have their own values already and in that case you may need to change those first.
Thanks so much for the great article.
I must say that I like the way you explain everything so that’s it’s very easy to understand.
Thanks so much.
Very good and usefull info about magento layout and blocks, I like it! Thanks a lot!
[…] have covered a lot about Magento’s layout XML in our past 2 articles [1] [2]. We saw that Magento will read the layout XML files in a specific order and that the local.xml file […]
Thank you for this great article!
[…] 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 […]
[…] have covered a lot about Magento’s layout XML in our past 2 articles [1] [2]. We saw that Magento will read the layout XML files in a specific order and that […]
Block Name and Alias <— I think that was maybe a headline not in the pinn about "least one output block" ?
Hi, great explanation. First one I’ve seen about non-output blocks. However, I’m still not clear on it.
For example I have a block that is:
The block appears after all other blocks in the “product.info” parent block.
I tried added before=”-” and before=”other_block_names” to the block type line above but it didn’t do anything.
I then found the parent block template uses a getChildHtml(‘other’) and displays this block, at the point where that getChildHtml() code is.
Effectively, the before attribute is irrelevant in this case.
Does this mean it is a non-output block?
Looking at the function for it
Does this mean that Mage_Core_Block_Template is a non-output block?
How do you know?
Your list of block types on the first part of this tutorial don’t identify whether they are non-output types. I can find any other references to your discussion of non-output blocks???
Thanks
Trav
@Travis
“before” and “after” attribute works only when children blocks are rendered automatically inside the parent block. For example, a parent block calls getChildHtml() method without argument:
Here, as explained in Part 1 of this tutorial, all children blocks will be rendered automatically. So in this case all children blocks are sorted and rendered according to “before” and “after” attributes of children blocks.
But if a parent block calls getChildHtml() with an argument, the first argument is taken as a child block name and only that block is rendered where getChildHtml() is called:
Here, child block with name ‘other’ is rendered where this method is called. So “before” and “after” attributes on children blocks has no effect as they are forced to be rendered at specific area.
Normally in Magento, a block is rendered by getChildHtml() method but this method is called only from parent blocks and it renders its children blocks. But there is always at least one block which is root and has no parent. So how the root block can be rendered? There is an example explained in this tutorial:
“root” block defines “output” attribute in its layout XML definition. The value of it is “toHtml”. Because “root” block has no parent block, it cannot be rendered by getChildHtml() method. So here, attribute output=”toHtml” tells Magento Layout Engine to call toHtml() method of this block and then render the returned string immediately.
Output blocks can be any block which defines “output” attribute in its layout XML definition. Output blocks has no connection with block type, it can be any type of block.
Great explanation. Thank you.
Very easy to understand. Thanks a lot.
[…] – http://magebase.com/magento-tutorials/digging-deeper-into-magentos-layout-xml-part-2/ […]
[…] – http://magebase.com/magento-tutorials/digging-deeper-into-magentos-layout-xml-part-2/ […]
This tutorial series is fantastic! Thank you.
I have some custom tabs that I have added to my product pages via local.xml. Can you tell me if there is a way to show/hide those tabs based on the products parent category using local.xml?
[…] Digging Deeper Into Magento’s Layout XML – Part 2 […]