Digging Deeper Into Magento’s Layout XML – Part 2

piaoling  2012-07-10 10:59:53

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:

  1. Initializing page layout
  2. 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:

  1. Instances of the Layout and Layout Update are created.
  2. Layout handles are added according to the $handles argument if passed.
  3. Store layout handle STORE_[store_code] is added to the Layout Update Instance. For example, if code of current store is en, then layout handle STORE_en is added.
  4. 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 is magebase and theme name for layout is modern, then the layout handle THEME_frontend_magebase_modern is added.
  5. 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’s category controller’s view action. So it will add an action handle catalog_category_view.
  6. All Layout XML files defined for all active modules are loaded
  7. If a layout file named local.xml exists in the current theme’s layout folder, it is loaded last
  8. Layout updates of all added layout handles from the loaded layout XML are merged
  9. Layout updates of all added layout handles from the database are merged
  10. If the $generateXML argument of loadLayout() method is passed as false, the initialization is finished.
  11. The layout update data is refined by removing all blocks and block references defined with the remove tag. (As discussed in Part 1)
  12. If $generateBlocks argument is passed as false, the initialization is finished.
  13. The instances of block classes are created according to the block definitions in the Layout XML
  14. 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:

  1. Remove the cart sidebar block from the right sidebar
  2. Insert a CMS block with identifier foo in the left sidebar
  3. 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.

Resources

类别 :  magento(258)  |  浏览(4594)  |  评论(0)
发表评论(评论将通过邮件发给作者):

Email: