This is part of a series on Magento Best Practices. Click here to read more.
Layouts round out the three component view layer of Magento. Layout is a set of instructions written in xml to define the layout of pages in Magento. They define the structure of a page. They also allow you to penetrate into the behavior of a page. Layouts do this by enabling the user to call any public method from any block right inside the layout xml. Layout is very powerful for this reason.
Layout was created (in part) so that frontend developers have access to the power of block classes without having to maintain and interface with them directly. This is one of the key parts of Magento that makes it such a powerful platform. Layout is made up of handles, blocks, actions, updates, references and more.
The principles to follow are:
- Know when to use the default handle
- Avoid code duplication by using handles
- Move blocks, don’t re-create them
- Use The Remove Directive Correctly
- Name Parameter Nodes Correctly
Know When to Use The Default Handle
Inside of layout xml, sets of instructions are defined inside of handles. These handles allow you to define where an instruction is to be parsed. There is a handle that is meant to be parsed on all default pages in Magento. This handle is the <default> handle. This is where you will see the standard structure of your theme come to life. Things such as Javascript, CSS, headers, left and right columns, and footers are all defined. This is because these elements stay relatively the same throughout your site. There is no need to define all of this for every single page.
Whenever you need to place something on every single page, you need to leverage the <default> handle to do so.
The important thing though, is not when you should use the <default> handle, but when you should not use it.
You should not use the default handle whenever the behavior you wish to introduce does not belong on every single page. A good example is when you create any sort of specific functionality that requires Javascript. If that functionality is not present on every page, that Javascript should not be declared in the default handle.
Whenever you have page specific functionality, put it into its appropriate handle. A full action handle works perfectly for this.
Use a full action handle, instead of the <default> handle to add checkout specific JS:
1 2 3 4 5 6 7 8 9 10 |
<layout version="0.1.0"> <checkout_onepage_index> <reference name="head"> <action method="addItem"> <type>skin_js</type> <name>js/my_checkout_logic.js</name> </action> </reference> </checkout_onepage_index> </layout> |
Leverage Custom Handles to Avoid Duplication
What if your code does not belong on every page, (ruling out using the default handle), but is present on multiple pages?
You want to avoid code duplication, but you don’t want to maintain it multiple areas. If you have a set of instructions that need to be present inside of 2+ areas, but not every area, you can leverage custom handles to help avoid duplication.
You can define a custom handle and place your logic inside of this handle. You can then use the <update> directive to place your layout instructions inside of other handles.
This allows you to maintain one set of instructions, but use them in multiple areas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<layout version="0.1.0"> <add_js_to_header> <reference name="head"> <action method="addItem"> <type>skin_js</type> <name>js/only_present_sometimes.js</name> </action> </reference> </add_js_to_header> <customer_account> <update handle="add_js_to_header"/> </customer_account> <cms_page_view> <update handle="add_js_to_header"/> </cms_page_view> <checkout_onepage_index> <update handle="add_js_to_header"/> </checkout_onepage_index> </layout> |
Move Blocks Instead of Duplicating Blocks
When wanting to relocate blocks, many developers will declare a new block inside its new parent, and specify the <remove> directive on the other block. So instead of moving the block to its new location, they instead “delete” the block and recreate it.
This is inefficient and forces you to maintain the blocks’ directions yourself. This is especially a pain if the block is a core block maintained by Magento. Every time you upgrade your site, you have to check your layout to see if Magento made any sort of changes to the block declaration, template file, etc.
If you move currently defined blocks, you don’t have to worry about maintaining them yourself.
Remember, you can call any public method from any block class. You can use the methods unsetChild() and insert() or append() to move around blocks from inside your layout.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<layout version="0.1.0"> <catalog_category_view> <reference name="right"> <action method="unsetChild"> <block>catalog.compare.sidebar</block> </action> </reference> <reference name="left"> <action method="insert"> <block>catalog.compare.sidebar</block> </action> </reference> </catalog_category_view> </layout> |
Use The Remove Directive Correctly
The <remove> directive is used to flag a block for exclusion. When this directive is used, the “ignore” flag is set on a block. When this flag is set, it cannot be unset.
When the layout is loaded and parsed, all of the directives are evaluated and those block declarations are removed from layout. This happens before the block classes are even instantiated, so you will not have access to them.
If you define a block to be removed, it is atomic and absolute. Do not specify a block to be removed just to add it again “your own way”. Instead, just manipulate the block you wish to alter by calling the <reference> directive. Now you can alter the block class directly. If you need to move a block, use the recommendations in this article.
Use Descriptive Parameter Names
When using the directive, you can call any public method from any block class in Magento. You can also pass parameters into these methods.
1 2 3 4 5 6 7 8 9 10 11 |
<layout version="0.1.0"> <customer_account_index> <reference name="root"> <action method="myBlockMethod"> <argument1>foo</argument1><!-- what is this? --> <argument2>bar</argument2><!-- I don't know what these are doing --> <argument3>baz</argument3><!-- now I have to go to the method to see --> </action> </reference> </customer_account_index> </layout> |
There is something important to note about this. The node naming of parameters are not considered when evaluating layout directives. The only thing that matters is the order in which they were defined.
This means that the first node becomes the first argument. The second node becomes the second argument, and so on.
By this definition, it does not matter on a technical level what you name your nodes in directives.
There is a catch. Magento isn’t the only entity to look at layout. You know who else does? YOU DO!
If you decide to name them any random string you want, it will make your code harder to maintain.
Your goal should always be to create easy to read and easy to maintain code. If you name your nodes non-descriptive names, you will not be able to easily understand what you are trying to accomplish. This is especially true when you try to look at it 6 months later. Or worse, when someone else has to maintain your code.
Always name your parameters descriptive names. I will always name them the arguments being passed into the method.
1 2 3 4 5 6 7 8 9 |
<layout version="0.1.0"> <customer_account_index> <reference name="root"> <action method="setTemplate"> <template>page/1-column.phtml</template><!-- I know exactly what this is now --> </action> </reference> </customer_account_index> </layout> |
Now you can go and create cleaner and easier to maintain layout xml for your themes. Have fun!
Great explanation, thanks!