Tuesday, November 20, 2012

Understanding Magento Layout Update XML

Magento
Note: The following article is constisted of pieces from more than one sources. You can view all of them at the end of the article.

In Magento, layouts are the XML files that define a given page within the store. The templates usually specify a module and a template to go with that block, allowing for standard modules (such as loading a products data) to be combined with custom templates to style the data.

In Magento a block is simply an imaginary container for something. A block can contain other blocks or it can contain the content itself.

<block type="page/html" name="root" output="toHtml" template="page/3columns.phtml">
     [...other stuff inside...]
</block>

The type attribute above is "page/html" and the name of the block is "root". This signifies that this is the outer ("root") block of the page and all other blocks will be contained within this one. The template attribute signifies that the template for this block is located at Magento/app/design/frontend/base/default/templates/page/3columns.phtml. If you open this file you will see in various parts of the template PHP code similar to:

<?php getChildHtml('head') ?>

The purpose of this code is to display the block with the name "head".

When a user visits a one-column page on your site, Magento first looks at the default layout and then it looks at the <page_one_column> layout. The <page_one_column> layout doesn't override the entire default layout, just the certain elements that it specifies.

To illustrate, look at part of the contents of the <page_one_column> tag:

<reference name="root">
     […Some stuff in here]
</reference>

This reference is referring to the "root" block defined in the default layout above. So anything inside of this reference is added to what is already in the block.

If you open checkout.xml (this layout controls the pages in the shopping cart section of your store), you'll notice the following piece of code:

<reference name="top.links">
    <block type="checkout/links" name="checkout_cart_link">
        <action method="addCartLink"></action>
                <action method="addCheckoutLink"></action>
    </block>
</reference>

The reason that these are <reference>'s and not <block>'s is that they are referring to blocks that have been previously defined in page.xml.

Now, looking below the closing </default> tag, the remaining tags, such as <checkout_cart_index> are layouts for the specific pages in the shopping cart section of the site.
So when visit the following page on your site: http://yoursiteurl.com/checkout/cart/, Magento looks at the layouts in the following order:

  1. page.xml – <default>
  2. page.xml – <page_one_column>
  3. checkout.xml – <default>
  4. checkout.xml – <checkout_cart_index>

Each time an additional layout is added to this list, it adds to or in some cases overrides the previous layouts in the list.

After the layout XML is loaded, Magento applies the default handle, store handle, theme handle and then action handle. At the very end, layout updates from the database are applied.

Each addition to the layout file injects content (or in fact could hide/modify content) within a template. The snippet of XML you need to add to insert content will specify a block and its type in order to get its data and likely a template to display that data.

The various layout files (each one coincides with a module) can, for example, add their own blocks to the left column. In this way the Checkout module adds the shopping cart to the right column and the Reports module adds the products viewed there as well.

layout-update-xml
Layout Update XML for Home Page


The most common use for this would probably be to add a CMS Static Block to one of the sidebars or similar. This would be done by adding the below XML to your layout. Remember this can be done for products and categories as well as CMS Pages – so it is an ideas way to cross-sell or add banners to pages.

To place a block in a sidebar:

  1. From the Admin panel, select CMS > Pages > Manage Content.
  2. Click to open the CMS page. Then, in the Page Information panel on the left, select Design.
  3. In the Layout Update XML box, enter the code for the right or left sidebar, as shown in example. Change the reference name to identify either the "left" or "right" sidebar and change the block_id to the identifer of the block to be placed.

<reference name="right">
  <block type="cms/block" name="right.permanent.callout">
    <action method="setBlockId"><block_id>your-block</block_id></action>
  </block>
</reference>

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.

Layout Elements

A layout handle may contain the following elements:

  • 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.

  • 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.

  • 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

  • remove: This element is used to remove an existing block from the layout. The block to be removed is specified with the name attribute.

  • 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.
      setTemplate: allows us to change the default .phtml file used in a particular block. For example by navigating to app/design/frontend/default/default/layout/catalog/product/view.xml we can see the reference:

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

      and by using another <template> value we are allowed to change default .phtml file used on our products page. Possible values are:

      • 1column.phtml
      • 2columns-left.phtml
      • 2columns-right.phtml
      • 3columns.phtml
      • one-column.phtml
      • dashboard.phtml

  • addCss: allows us to add an additional CSS file to our page on per-page basis or globally for our template. If we use a reference name "head" and action method addCss by using:

    <reference name="head">
        <action method="addCss"><link>style.css</link></action>
    </reference>

    then our page will have an additional line of code to attach the CSS file.
    The <link> path refers to the /skin/frontend/default/default/css/ folder.

  • addJs: allows us to attach a .js script in the same way as we attached a CSS file. The script path refers to the /js/your_theme/ folder but you can specify any subdirectory of /js/.

    <reference name="head">
        <action method="addJs">test/script.js</action>
    </reference>

  • setContentType: this method can override default headers sent by our page to the browser. So we can set a text/xml instead of text/html (or another as we wish)

    <reference name="head">
        <action method="setContentType"><content>text/xml</content></action>
    </reference>

  • setCharset: allows us to override default page encoding on per-page basis or globally. As long as we are saving our files with proper encoding this will not be necessary.

    <reference name="head">
        <action method="setCharset"><charset>ISO-8859-2</charset></action>
    </reference>

  • addLink: can be used to set a setting to which we can refer in our .phtml template files also without manually changing the .phtml files.

    Example usage: By adding a block which was found in app/design/frontend/default/default/layout/customer/account.xml into our <reference name="content"> in app/design/frontend/default/default/layout/checkout/cart.xml we can allow the customer to skip to the account information directly from the cart.

    <block type="customer/account_navigation" name="customer_account_navigation" before="-">
        <action method="setTemplate">
            <template>customer/account/navigation.phtml</template>
        </action>
        <action method="addLink" translate="label">
            <name>account</name>
            <path>customer/account/</path>
            <label>Account Dashboard</label>
        </action>
        <action method="addLink" translate="label">
            <name>address_book</name>
            <path>customer/address/</path>
            <label>Address Book</label>
        </action>
        <action method="addLink" translate="label">
            <name>account_edit</name>
            <path>customer/account/edit/</path>
            <label>Account Information</label>
        </action>
    </block>


  • 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.


  • <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:

    <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>

    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.

    Block Types

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

    • 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.

    • page/html: This is a subtype of core/template and defines the root block. All other blocks are child blocks of this block.

    • page/html_head: Defines the HTML head section of the page which contains elements for including JavaScript, CSS etc.

    • page/html_header: Defines the header part of the page which contains the site logo, top links, etc.

    • 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.

    • 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.

    • 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.

    • page/html_breadcrumbs: This block defines breadcrumbs on the page.

    • page/html_footer: Defines footer area of page which contains footer links, copyright message etc.

    • core/messages: This block renders error/success/notice messages.

    • 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.

    Standard Block Layout

    In the following diagram, the block names that can be used to refer to a block in the layout are black and the block types, or block class paths, are blue.

    title=

    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.

    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.

    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">

    * 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 ordering with after and before attributes

    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"/>

    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.

    Snippets

    Display static blocks with custom layout update

    <reference name="left">
        <block type="cms/block" name="Footer" before="-">
            <action method="setBlockId"><block_id>footer_links</block_id></action>
        </block>    
    </reference>

    Display Layout Handles

    $this->loadLayout(); echo "<pre>"; print_r($this->getLayout()->getUpdate()->getHandles()); echo "</pre>"; die();

    Insert CMS blocks into template (.phtml) files without any layout modifications

    <?php echo $this->getLayout()->createBlock('cms/block')->setBlockId('your_cms_block_identifier')->toHtml() ?>

    Sources

    Jim Code - "Using Layout Update XML in Magento"
    Magebase - "Demystifying Magento's Layout XML – Part 1"
    Magebase - "Digging Deeper Into Magento's Layout XML – Part 2"
    Magento - "Making XML Layout Updates"
    Snipplr