MVC Developers Part 3 – Layouts, blocks, and templates

Continuing with Magento, we will skip models for the moment, and jump to the right of our flowchart to look at layouts and Blocks.

Unlike many popular PHP MVC systems, Magento’s Action Controller does not pass a data object to the properties to see whether the view object.

Instead, the View component of the Model, View, Controller models directly references the information they need to get to play.

One consequence of this design decision is that the View is divided into Blocks and templates. Blocks PHP objects, templates “raw” PHP files that a mix of HTML and PHP (PHP which is used as a templating language contain). Each block is linked to a template file. Within a phtml file, $ this PHP keyword will contain a reference to the template block object.

A quick example. Take a look at the standard product template

File: app/design/frontend/default/default/template/catalog/product/list.phtml
//..

You see the following PHP code template.

<?php $_productCollection=$this->getLoadedProductCollection() ?>    <?php if(!$_productCollection->count()): ?> <div>
    <?php echo $this->__("There are no products matching the selection.") ?>    </div>
<?php else: ?>

GetLoadedProductCollection the method can be found in the template block, Mage_Catalog_Block_Product_List

File: app/code/core/Mage/Catalog/Block/Product/List.php
...
public function getLoadedProductCollection()
{
    return $this->_getProductCollection();
}
...

The block is _getProductCollection instantiates models and then reads their data, returning the result to the template.

Nesting Blocks

The real strength of blocks / templates with the getChildHtml method. This can include the contents of a secondary block / template inside of a primary block / Template.

Blocks Blocks Blocks phone call is how the entire HTML layout for your page was created. Take a look at the layout of a template column.

File: app/design/frontend/default/default/template/page/one-column.phtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>">
<head>
<?php echo $this->getChildHtml('head') ?>
</head>
<body>
    <?php echo $this->getChildHtml('content') ?>
    <?php echo $this->getChildHtml('before_body_end') ?>
    <?php echo $this->getAbsoluteFooter() ?>
</body>
</html>

The template itself is only 11 lines long. However, each call to $ this-> getChildHtml (…) will make another block. These blocks will in turn, make use getChildHtml other blocks. Blocks It is completely down.

The Layout

So, blocks and templates are all well and good, but you’re probably wondering

1. How do I tell Magento Blocks, I want to use on a page?
2. How do I tell Magento Block, which I should start rendering with?
3. How do I create a block in getChildHtml (…)? These strings do not seem Block names for me.

This is where the layout object in the photo. The layout is an XML string that will determine which blocks are included on one page, and Block (s) to kick off the return.

Last time we were echo content directly from action methods. This time we make a simple HTML template for our Hello World module.

First create a file

app/design/frontend/default/default/layout/local.xml

with the following contents

<layout version="0.1.0">
    <default>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" />
        </reference>
    </default>
</layout>

Then, create a file at

app/code/local/Alanstormdotcom/Helloworld/simple_page.phtml

with the following contents

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Untitled</title>
    <meta name="generator" content="BBEdit 9.2" />
    <style type="text/css">
        body {
            background-color:#f00;
        }
    </style>
</head>
<body>

</body>
</html>

Finally, each Action Controller is responsible for the kick-off of the layout process. We must add two method calls to the action method.

public function indexAction() {
    //remove our previous echo
    //echo 'Hello Index!';
    $this->loadLayout();
    $this->renderLayout();
}

Clear your cache and load your Magento Hello World controller page. You should now see a website with a bright red background and an HTML source that matches what simple_page.phtml.

What is Going On

So, that’s a lot of voodoo and cryptic incantations. Let’s look at what is happening. First you need to Layout Viewer module. This is a module similar to the Config Viewer module that will let us peek at some of Magento’s internals.

Once you have installed the module (similar to how the module Config Viewer Setup), go to this URL

http://example.com/helloworld/index/index?showLayout=page

This is the layout for your page xml / request. It is composed of , and tags. When you call the load method of your Action Controller Layout will Magento
1. Generate the XML layout
2. A copy of one class for each block and tag, looking up the class using the tag’s name attribute as a global config path and store in the internal layout of the array _blocks layout object.
3. If the tag contains a output attribute, the value for the internal _output array of the layout object.
Then, when you render method in your Action Controller Layout call, Magento will iterate over all blocks in the _blocks array, where the output value of the attribute as a callback method. This is almost always toHtml, and means the starting point for the output will be to Block Template.
The following sections will cover how Blocks are instantiated, how this layout file is generated, and ends with kick-off of the output process.

Block Instantiation

So, in a layout XML file, or a has a “type” which is actually a URI

<block type="page/html" ...
<block type="page/template_links"

The URI refers to a location (say with me) the global configuration. The first part of the URI (in the above examples page) will be used to query the global configuration to the page class name to search. The second part of the URI (in the above two examples, html template_links) will be added to the base class name to the class to instantiate to create Magento.

We go through page / html as an example. First Magento looks for hub in the global config

/global/blocks/page 

and finds

<page>
    <class>
        Mage_Page_Block
    </class>
</page>

This gives us the base class name Mage Page Block. Then the second part of the URI (html) added to the class name to give us the final name of the class MagePageBlock_Html Block. This is the class that will be instantiated.

Blocks are grouped by the class names in Magento, everything that a similar method to share instantiation. This concept will be discussed in more detail in a later chapter.

Are we satisfied UNTIL Pages / html as an example. First Magento right is there for the global config node in

Difference between <block /> and <reference />

We said that both and Block will instantiate classes, and you’re probably wondering what the difference is.

‘s are used to replace existing blocks in a layout file. For example, consider the following fragment layout.

<block type="page/html" name="root" output="toHtml" template="page/2columns-left.phtml">
    <!-- ... sub blocks ... -->
</block>
<!-- ... -->
<reference name="root">
    <block type="page/someothertype" name="root" template="path/to/some/other/template" />
    <!-- ... sub blocks ... -->
    </block>
</reference>

Magento is primarily a page / html Block named root. Then, if they later reference with the same name (root meetings), will replace the original root with embedded in the .

This is what we have done in our local.xml file above.

<layout version="0.1.0">
    <default>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" />
        </reference>
    </default>
</layout>   

Block called the root, has been replaced with our Block, who points out another phtml template file.

How Layout Files are Generated

So, we have a slightly better understanding of what happens to the layout XML, but where is this XML file? To answer that question, we must introduce two new concepts, handles and packaging.

Handles

Each page request in Magento will generate a number of unique handles. View the Layout module can show you this opportunity with a URL something like

THIS is what we did in our local.xml file above.

http://example.com/helloworld/index/index?showLayout=handles

You will see a list similar to the following (depending on your configuration)

1. default
2. STORE_bare_us
3. THEME_frontend_default_default
4. helloworld_index_index
5. customer_logged_out

Each of these has a handle. Handles in a variety of locations within the Magento system. The two we focus on are standard and helloworld_index_index. Standard Handle is available in each application system in the Magento. Helloworld_index_index the Handle is created by combining the frontal Name (Hello World), Action Controller name (index), Action Controller and Action Method (index) into a string. This means that every possible method to an Action Controller has a handle associated with it.

Remember that “Index” is the standard for both Magento Action Controllers and Action methods, so the next request

http://example.com/helloworld/?showLayout=handles

Will also handle named helloworld_index_index

Package Layout

You can think of the packaging similar to the global configuration. It is a large XML file containing all the possible layout for a particular configuration includes installing Magento. Let’s take a look at using Layout View Module

http://example.com/helloworld/index/index?showLayout=package

This may take a while to load. If your browser is choking on the XML rendering, try the text format

http://example.com/helloworld/index/index?showLayout=package&showLayoutFormat=text

You will see a very large XML file. This is the package layout. This XML file is created by combining the contents of all XML files for the current layout theme (or package). For the default install, this is at

app/design/frontend/default/default/layout/

Behind the scenes there is a part of the overall configuration nodes contain all the file names to load. Once the files are included in the configuration are combined, Magento will merge into a final XML file, local.xml. This is a file where you are able to add your changes to your Magento install.

Combining Handles and The Package Layout

So, if you look at the package layout, you will see some familiar tags like and , but they’re all surrounded by tags that look like

<default />
<catalogsearch_advanced_index />
etc..

These are all handle tags. The layout for an individual request is generated by grabbing all parts of the package layout that handles all the request for competition. So in our example above, our layout is generated by grab tags from the following sections

<default />
<STORE_bare_us />
<THEME_frontend_default_default />
<helloworld_index_index />
<customer_logged_out />

There is an extra tag, you must be aware of the packaging. The tag you can handle the other tags. For example

<customer_account_index>
    <!-- ... -->
    <update handle="customer_account"/>
    <!-- ... -->
</customer_account_index>

Says that the applications should handle a customer account index s, and s from the Handle.

Applying What We’ve Learned

OK, that’s a lot of theory. Let’s get back to what we used to. Knowing what we know now, to add

<layout version="0.1.0">
    <default>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" />
        </reference>
    </default>
</layout>

means that we will prevail at the “root” tag local.xml. with another block. This in Handle, we ensured that this will be done to eliminate every page request in the system. That is probably not what we want.

If you go to another page in your Magento site, you will find that they are either blank, white, red or have the same background that you do hello world page. Let’s change your local.xml file so it applies only to the Hello World page. We do this by changing the default to the full name of the action usage (helloworldindexindex).

<layout version="0.1.0">
    <helloworld_index_index>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml" />
        </reference>
    </helloworld_index_index>
</layout>

Create your Magento cache, and the rest of your pages should be restored.

At present this applies only to our index action method. Let’s add to the goodbye action means. In your Action Controller, the parting action to change so that it appears

public function goodbyeAction() {
    $this->loadLayout();
    $this->renderLayout();
}  

)

If you suffer from the following URL, you will find that you still have the standard Magento layout.

http://example.com/helloworld/index/goodbye

We need a handle to add the full name of the action (helloworldindexgoodbye) to our local.xml file. Rather than specifying a new you can use to update the tag helloworld_index_index Handle contest.

<layout version="0.1.0">
    <!-- ... -->
    <helloworld_index_goodbye>
        <update handle="helloworld_index_index" />
    </helloworld_index_goodbye>
</layout>

Loading the next page (after clearing your cache Magento) must now produce identical results.

http://example.com/helloworld/index/index
http://example.com/helloworld/index/goodbye

Starting Output and getChildHtml

In a standard configuration, the output starts at the root block is called (it has an output attribute). We have written our own root template

template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml"

Templates are made from the root of the current theme. In this case, that is

app/design/frontend/default/default

so we have to climb five folders (../../../../../) then click through to our custom page. Most templates are saved in Magento

app/design/frontend/default/default/templates

Adding Content Blocks

A simple red page is pretty boring. Let’s use some add content to this page. Change your   <helloworldindexindex /> Handle local.xml so it appears in the following.

<helloworld_index_index>
    <reference name="root">
        <block type="page/html" name="root" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml">
            <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>
        </block>
    </reference>
</helloworld_index_index>

We add a new block nested within our roots. This is a piece that is distributed with Magento and will display a customer registration form. Because this block nests within our root block, we made it available to be drawn into our simple_page.html Template. Then we will approach the getChildHtml Block to use our simple_page.phtml file. Edit simple_page.html so it looks like this

<body>
    <?php echo $this->getChildHtml('customer_form_register'); ?>
</body>

Clear your cache Magento and reload the page and you have the customer on your registration form red background. Magento has a block named top.links. Let’s try including that. Edit your file so that it reads simple_page.html

<body>
    <h1>Links</h1>
    <?php echo $this->getChildHtml('top.links'); ?>
</body>

When you reload the page, you will notice that your <h1> Links </ h1> title rendering, but nothing has been rendering for top.links. That’s because we do not add to local.xml. GetChildHtml the method can include blocks as sub-blocks in the layout. This makes Magento only instantiate the blocks it needs, and you can make a difference for Blocks templates based on the context.

Let us add to our top.links Block local.xml

<helloworld_index_index>
    <reference name="root">
        <block type="page/html" name="root" template="../../../../../code/local/Alanstormdotcom/Helloworld/simple_page.phtml">
            <block type="page/template_links" name="top.links"/>
            <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>
        </block>
    </reference>
</helloworld_index_index>

Clear your cache and reload the page. You will see the top.links module.

Wrapup

This applies Layout fundamentals. If you found it somewhat daunting, do not worry, you will rarely need to work with layouts on such a fundamental level. Magento offers several pre-built layouts that can be edited and skinned on the needs of your shopping requirements. Understand how the whole layout system works can help a lot if you annoying layout problems that are part of a Magento project.

(Based on Alanstorm Turtorial)

Comments

Leave a Reply

Your email address will not be published.

eighteen − 8 =

Security Code: