Codegento Who Let Mage Out Of The Cage?

26Mar/1123

How Blocks And PHTML Work Together

Early on in my Magento-coding days, I struggled with the relationship between the block classes and the phtml files. Which came first? Who owned who? Do you need blocks? Do you need phtml? First and foremost, we need to understand that the complexity of the layout/block/phtml relationship is necessary, powerful, and flexible. Those three pieces allow for the ability to "theme" Magento and to easily override pieces of the store's look and feel without messing with the default layout/theme.

So where do we start? How about with this statement: Phtml files are owned and used by blocks. In the code, however, they are called "templates", not "phtml". Each block instance has 0-1 templates assigned to it (either through the layout XML files or hard coded in the block code itself).

Assigned in the layout file:

<block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml">

Assigned in the code:

public function __construct()
{
        parent::__construct();
        $this->setTemplate('catalog/product.phtml');
}

So how does the flow work? It all starts with the layout files. Calling them layout files make sense in some cases, but the layout files don't always define the "layout" of the page. Actually, more often than not, the phtml does that. With that said however, the layout files define WHAT can be blocks/phtml are AVAILABLE. For example, lets say you have a snippet of your layout file that looks like this:

<reference name="content">
    <block type="awesome/outer_view" name="outer.view" template="awesome/outer/view.phtml">
        <block type="awesome/inner_view" name="inner.view" template="awesome/inner/view.phtml"/>
    </block>
</reference>

What this IS saying is:

  1. We are going to add the "outer.view" block to the already existing "content" block (aka: outer.view is now a child of content).
  2. outer.view's phtml WILL be rendered automatically (because content's block renders ALL of it's child blocks)
  3. Because "inner.view" is within the ""outer.view" block, the "inner.view" is now a child of "outer.view"

What this IS NOT saying:

  1. inner.view's phtml will automatically be rendered (This depends on it's parent - outer.view).

So how do we know what phtml will be rendered, and what won't? The answer is - it ALWAYS depends on the parent block. The initial loading and rendering of the block(s) is done in the controller action. The action will generally contain something like:

$this->loadLayout();
$this->renderLayout();

The loadLayout() looks through all of the appropriate layout files and creates one big layout of the page. Open up page.xml right now to aid in the understanding of the rest of this article. The block "root" is the Great Great Great Great Great Grandparent block. It all starts with him.

The renderLayout() begins by

  1. Calling the toHtml() function on the "root" block (Mage_Page_Block_Html)
  2. The toHtml() ends up rendering the phtml that was assigned to the "root" block (page/3columns.phtml)
    • While rendering the page/3columns.phtml, the rendering agent comes across the line: echo $this->getChildHtml('head'). Whenever we see $this inside phtml, it is always referring to the block that the phtml has been assigned to.
  3. So now we need to see if "head" is a child block of $this (which is "root"). If we look at the page.xml we see that "head" IS a child, so we will now call the toHtml() function on the "head" block (Mage_Page_Block_Html_Head)
  4. The toHtml() ends up rendering the phtml file that is hard-coded in the block's constructor which is (page/html/head.phtml).
  5. This type of flow goes ON and ON until we pick it up again at the "content" block which happens to be a Mage_Core_Block_Text_List block.
  6. The toHtml() function will be called on this block, but there is no phtml assigned to it anywhere. Instead, the toHtml() function just says "loop through all of my children and render them one right after another".
  7. If we are sticking with our example up above, we have added 1 child block to "content": "outer.view". We will assume that "awesome/outer_view" maps to Super_Awesome_Block_Outer_View and that Super_Awesome_Block_Outer_View extends Mage_Core_Block_Template.
  8. So now the toHtml() gets called on Super_Awesome_Block_Outer_View so as long as we haven't overridden the _toHtml() function it will render the phtml that is assigned to it (awesome/outer/view.phtml).

Assuming awesome/inner/view.phtml looks like this:

<div>Inner View</div>

If the awesome/outer/view.phtml looks like this:

<div>Start Outer</div>
<div>End Outer</div>

It will NEVER render the inner.view and will look like this:

Start Outer
End Outer

If the awesome/outer/view.phtml looks like this:

<div>Start Outer</div>
<?php echo $this->getChildHtml('inner.view') ?>
<div>End Outer</div>

It WILL render the inner view in between the two Start and End divs like this:

Start Outer
Inner View
End Outer

If the awesome/outer/view.phtml looks like this:

<div>Start Outer</div>
<?php echo $this->getChildHtml() ?>
<div>End Outer</div>

It WILL render the inner view (and any other child blocks) in between the two Start and End divs like this:

Start Outer
Inner View
End Outer

We can't assume that just because the block is defined in the layout that it will be rendered. Either the toHtml() needs to explicitly render the child blocks, or the phtml assigned to the block must render the child blocks via the getChildHtml() function.

So, recapping what we learned:

  • Block are what drive the output to the browser.
  • Templates/Phtml are assigned to blocks
  • The parent/child relationships of blocks are defined in the layout files (but can be in the code to if a block calls the setChild() function in itself).
  • The parent block determines how/when/if the child block is rendered (generally through the "echo $this->getChildHtml()" functionality.
  • Any time you see $this in a phtml file, the $this object is the instance of the block you are currently rendering.

Posted by Ben Robie

Comments (23) Trackbacks (0)
  1. awesome post Ben! I think it’s the best layout/design/blocks explanation I’ve ever seen. thanks for this.
    Just a little typo, when you say: Assuming awesome/outer/view.phtml looks like this:(just after the 8 items listof what the loadLayout() method does) I think you meant awesome/outer/view.phtml
    see you in my reader :)

  2. It is very very usefull. Excellent……………….. super………………………………….

  3. Just started with magento. Best amongst all resources available to explain the blocks reltionship.
    Loads of Thanks

  4. Yep, after 8 hours of searching for a good way to understand it, your post makes the work much more clear! Sweet. Thank YOU!

  5. thanks for making it more clear. that worked for me. but now i have 2 blocks in my root. like that:

    in the blocks.php only one of the two “tohtml” functions get loadet, but both “cunstruct” functions. so i see no html… can you give me a hint?

  6. You say : ” outer.view’s phtml WILL be rendered automatically (because content’s block renders ALL of it’s child blocks) ”

    and then you say : ” What this IS NOT saying: 1. inner.view’s phtml will automatically be rendered ”

    But inner.view is a child of outer.view, this is confusing !!

    is head not a Structural bloc ?

    • inner.view is not a “direct child” of the content block. The only way for inner.view to be rendered is if it is 1) explicitly rendered from the outer.view, or
      2) if outer.view would have a foreach that would print out all of its children (which it doesn’t).

      • So, you mean the content block is more special.

        There is something i dont understand, if you have

        can you do unsetChild on the inner.view bloc (not ) ?

        • The content block isn’t “more special”, just different. If you look at the definition of the content block in the page.xml layout file, it is of type “core/text_list”, and isn’t assigned a template (like most blocks). The block Mage_Core_Block_Text_List actually implements the _toHtml() to NOT output a template, but instead loop through all of the children and call THEIR toHtml() functions.

      • xml was eaten i see

        if you have reference name=”content”
        block type=”awesome/outer_view” name=”outer.view” template=”awesome/outer/view.phtml”>
        block type=”awesome/inner_view” name=”inner.view” template=”awesome/inner/view.phtml”/>
        /block>
        /reference>

  7. just amazing!!! thanks a lot

  8. TX for the nice article!!!

  9. This article helps me to learn magento. I have target that I will learn magento. But I was failed. This article saves me to work on magento after a year. I was searching this type of article throughout this year finally got it. Thanks for sharing this kind of nice tutorial.

  10. Thanks a lot Ben. This is the best article I have read among all articles and books I have been reading for Magento and Zend framework from last 2 weeks. It helped me a lot in getting magento layout structure in simple words. Thanks a lot. :)

  11. Oh man…..this really really helped a lot. I was so damned confused. Thnk u soooo much!

  12. The best article on blocks till now.
    Keep up the good work :)

  13. Really the best article on blocks, even some intial paragraphs made it clear to me how these things work.
    Appreciate your work

  14. Really The best article on blocks till now.
    Keep up the good work :)


Leave a comment

(required)

Trackbacks are disabled.