Demystifying Magento’s Layout XML – Part 1

piaoling  2012-07-10 11:04:25

Demystifying Magento’s Layout XML – Part 1

The ‘View’ of Magento’s MVC pattern implementation is divided in two parts: Layout and Template. The template represents a HTML block while layout defines the location of the block on a web page. Magento provides ultimate flexibility and re-usability of design by layouts defined in XML.

Layout XML

Layout XML files can be found in app/design/frontend/[package]/[theme]/layout. Each Magento module may define its own layout XML file in the config.xml file.

1
2
3
4
5
6
7
8
9
<frontend>
  <layout>
    <updates>
      <mymodule>
        <file>mymodule.xml</file>
      </mymodule>
    </updates>
  </layout>
</frontend>

Each layout XML file represents its own design update. For example, catalog navigation is part of the Mage_Catalog module and Mage_Catalog module defines a layout update file: catalog.xml. So the block for the catalog navigation and its location on the page is defined in catalog.xml.

Before the page is rendered, Magento loads all configured layout update files and its design updates to determine which block is to be rendered at which location.

Here is an example layout XML file contacts.xml for the Mage_Contacts module. The root element of any layout XML file is <layout>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<layout version="0.1.0">
  <default>
    <reference name="footer_links">
      <action method="addLink" translate="label title" module="contacts" ifconfig="contacts/contacts/enabled"><label>Contact Us</label><url>contacts</url><title>Contact Us</title><prepare>true</prepare></action>
    </reference>
  </default>
 
  <contacts_index_index translate="label">
    <label>Contact Us Form</label>
    <reference name="root">
      <action method="setTemplate"><template>page/2columns-right.phtml</template></action>
      <action method="setHeaderTitle" translate="title" module="contacts"><title>Contact Us</title></action>
    </reference>
    <reference name="content">
      <block type="core/template" name="contactForm" template="contacts/form.phtml"/>
    </reference>
  </contacts_index_index>
</layout>

Layout Handles

First level child elements of the <layout> node are called layout handles. Each layout handle represents an update to the page layout. It may define new blocks to be included in a page at a specific location or remove a specific block from the page. It may also define the modifications in existing blocks already included in the page by other layout XML files.

After Magento loads all layout XML files, it determines which layout handles need to be processed. Normally, the layout handle is selected based on the controller action being executed. In most cases, Magento loads the layout handle with name: [module_front_name]_[controller_name]_[action_name].

For example, when the contact us page is requested, then index action of index controller of Mage_Contacts is executed. Module front name of Mage_Contacts is contacts. So the layout handle to be processed for the contact us page is contacts_index_index.

For any page, Magento always processes the default layout handle. So the updates defined in default handle are processed for every page regardless of which part of the site we are browsing. As the example above illustrates, the Mage_Contacts module requires a link to the “Contact Us” page in the footer on every page so the specific elements for it is defined in the default handle. These elements are discussed further in the next section.

Layout Elements

A layout handle may contain the following elements:

  1. label: This element is introduced since Magento 1.4. It defines the label of the handle which is shown as a descriptive reference in some areas of the admin panel.
  2. reference: This element is used to link an already defined block in any layout XML. To add any child block to an existing block, to modify attributes of an existing block or to perform any action on an existing block, the reference element is used to link to the existing block. The reference element must have a name attribute which refers to the existing block’s name.
  3. block: This element is used to define a new block. This element is usually defined inside a reference element when we want to create a new block. The block element must have a name attribute, which is a unique identifier of the block in the layout and a type attribute, which defines the block class name. If the block is of type or subtype of core/template, it can also have the template attribute which defines the actual phtml template file to be used for rendering the block
  4. remove: This element is used to remove an existing block from the layout. The block to be removed is specified with the name attribute.
  5. action: This element defines an action to be performed on the referenced or newly defined block. An action is simply a method of the block instance on which it is to be executed. The method attribute defines the method name in the block instance and all child elements of the action element are treated as parameters to the method. This element can be placed inside reference or block elements.
  6. update: This element loads an existing layout handle into the current layout handle. It provides a kind of inheritance of layout handles. It must have the handle attribute, which defines the handle of the block to be included.
    For example, all customer account pages have a similar layout and apply some common layout updates. So instead of defining all updates for the individual pages, a common layout handle: customer_account is defined in customer.xml as:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <customer_account translate="label">
      <label>Customer My Account (All Pages)</label>
      <!-- Mage_Customer -->
      <reference name="root">
        <action method="setTemplate"><template>page/2columns-left.phtml</template></action>
      </reference>
     
      <reference name="content">
        <block type="page/html_wrapper" name="my.account.wrapper">
          <action method="setElementClass"><value>my-account</value></action>
        </block>
      </reference>
     
      <reference name="left">
        <block type="customer/account_navigation" name="customer_account_navigation" before="-" template="customer/account/navigation.phtml">
          <action method="addLink" translate="label" module="customer"><name>account</name><path>customer/account/</path><label>Account Dashboard</label></action>
          <action method="addLink" translate="label" module="customer"><name>account_edit</name><path>customer/account/edit/</path><label>Account Information</label></action>
          <action method="addLink" translate="label" module="customer"><name>address_book</name><path>customer/address/</path><label>Address Book</label></action>
        </block>
        <block type="catalog/product_compare_sidebar" name="catalog.compare.sidebar" template="catalog/product/compare/sidebar.phtml"/>
        <block type="sales/reorder_sidebar" name="sale.reorder.sidebar" as="reorder" template="sales/reorder/sidebar.phtml"/>
        <remove name="tags_popular"/>
      </reference>
    </customer_account>

    This handle is then loaded on all account page handles. Here is an example of the customer address page handle in the same customer.xml file:

    1
    2
    3
    4
    5
    6
    7
    8
    <customer_address_index translate="label">
      <label>Customer My Account Address Book</label>
      <!-- Mage_Customer -->
      <update handle="customer_account"/>
      <reference name="my.account.wrapper">
        <block type="customer/address_book" name="address_book" before="-" template="customer/address/book.phtml"/>
      </reference>
    </customer_address_index>

    So when, the customer address page is rendered, the layout blocks defined in customer_account are also applied to the page.

Rendering Process

Before rendering the page, all block elements defined in the layout are instantiated. Nested block elements define child blocks. If any block element defines an output attribute, then it is considered as an output block. Only the output blocks are rendered and added to the response. All other child blocks are rendered only if they are called by the parent block. Let’s see how this works.

In Magento’s default theme, the block root is defined as an output block. This block is defined in the page.xml file. With the particular value of the template attribute, this block defines the page template to be used when rendering it, i.e. 1 column, 2 columns with left sidebar, 2 columns with right sidebar, 3 columns etc. By default, the 3 columns template is assigned to the page. There are also other child blocks defined under root like head, header, breadcrumbs, left, right, content, footer etc. These child blocks are rendered in the root template file (3columns.phtml) by calling something like this:

echo $this->getChildHtml('header');

In any template, the child blocks can be rendered by calling the getChildHtml() method as above and passing the child block name as the first argument. If the method is called without arguments, it will render all child blocks of the current block that are defined in the layout XML for that block.

Hence, Magento processes layout using a recursive rendering process. First the root block then its child blocks and then the child’s child blocks and so on.

Putting it all together

Let’s look at the example of contacts.xml file. The goal of this layout update file is to add a footer link to the contact us page if contact us is enabled in the system configuration and define the page structure of the “Contact Us” page.

There is already a block footer_links defined in page.xml file to display links in page footer. The footer_links block has an addLink method to add new links within the block. So here, the existing footer_links block can be referenced and the addLink action can be called on it with certain parameters to add a “Contact Us” page link to it. As the “Contact Us” page link needs to be added in the footer for every page this should be defined in the default handle.

1
2
3
4
5
<default>
  <reference name="footer_links">
    <action method="addLink" translate="label title" module="contacts" ifconfig="contacts/contacts/enabled"><label>Contact Us</label><url>contacts</url><title>Contact Us</title><prepare>true</prepare></action>
  </reference>
</default>

Here, default handle contains a reference to the footer_links block and performs an action addLink on it by passing the parameters label, url, title, etc. Also, in this case, the attribute translate is used to define which parameters need to be translated before passing them to the method. In the above example, label and title are translated using the current locale. The translation will be searched with the module scope defined in the attribute module. The attribute ifconfig defines the system configuration key to be checked before performing the method action. The system configuration key used as value of ifconfig, are normally defined as Yes/No type configuration values and return a Boolean value. If configuration returns false, the action will not be performed. In our case, if value of contacts/contacts/enabled is false, i.e. Contact Us is disabled from system configuration, the action to add the link will not be performed and the Contact Us link will not appear in the footer. ifconfig can only be used within an action element and only when the action needs to be performed if a particular configuration value is set to ‘Yes’.

1
2
3
4
5
6
7
8
9
10
<contacts_index_index translate="label">
  <label>Contact Us Form</label>
  <reference name="root">
    <action method="setTemplate"><template>page/2columns-right.phtml</template></action>
    <action method="setHeaderTitle" translate="title" module="contacts"><title>Contact Us</title></action>
  </reference>
  <reference name="content">
    <block type="core/template" name="contactForm" template="contacts/form.phtml"/>
  </reference>
</contacts_index_index>

As mentioned before, Magento automatically determines the handle to be loaded on the basis of a current action being executed. In case of the contact us page, it would call the Mage_Contacts module, IndexController and indexAction. The front name of Mage_Contacts is contacts. So the layout handle would be contacts_index_index. The label is given as Contact Us Form to identify the page in some areas of admin panel, for example the widget instance creation form:

Layout Handle Label

Next, the Contact Us page should have two columns with right sidebar. As explained in the previous section, the page template is defined by the root node and by default uses the 3 column template. So, to modify this for the Contact Us page, the root node is referenced here using the reference element. To assign a different template to the page, the setTemplate method should be called. So, an action element is defined to call the setTemplate method with the desired template path as a parameter:

1
2
3
<reference name="root">
  <action method="setTemplate"><template>page/2columns-right.phtml</template></action>
</reference>

Finally, in the main content area, a contact form should be displayed. To add the contact form block, the content block is referenced and a new block element is defined to include the contact form:

1
2
3
<reference name="content">
    <block type="core/template" name="contactForm" template="contacts/form.phtml"/>
  </reference>

Block Types

Magento defines some built-in block types which are widely used in layout.

  1. core/template: This block renders a template defined by its template attribute. The majority of blocks defined in the layout are of type or subtype of core/template.
  2. page/html: This is a subtype of core/template and defines the root block. All other blocks are child blocks of this block.
  3. page/html_head: Defines the HTML head section of the page which contains elements for including JavaScript, CSS etc.
  4. page/html_header: Defines the header part of the page which contains the site logo, top links, etc.
  5. page/template_links: This block is used to create a list of links. Links visible in the footer and header area use this block type.
  6. core/text_list: Some blocks like content, left, right etc. are of type core/text_list. When these blocks are rendered, all their child blocks are rendered automatically without the need to call the getChildHtml() method.
  7. page/html_wrapper: This block is used to create a wrapper block which renders its child blocks inside an HTML tag set by the action setHtmlTagName. The default tag is <div> if no element is set.
  8. page/html_breadcrumbs: This block defines breadcrumbs on the page.
  9. page/html_footer: Defines footer area of page which contains footer links, copyright message etc.
  10. core/messages: This block renders error/success/notice messages.
  11. page/switch: This block can be used for the language or store switcher.

This is a list of only commonly used block types. There are many other block types which are used in advanced theme implementations.

Summary

Layout XMLs make the theme design modular by using layout handles. These are just the basics of layout XML in Magento. More advanced implementation can be done by utilizing layout handles and actions. We will cover some more advanced usage of layout XML in next article of this series.

Articles in this series

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

Email: