Mini Tutorial:
Understanding Magento Blocks – The Basics

Magento has a comprehensive template engine where those who are more familiar with flat PHP files or a more traditional MVC framework may have a little trouble getting a good handle on. This Mini Tutorial will walk you through a progression of what you can expect when traditional PHP and MVC framework gets applied to Magento. I will briefly go over pseudo-code examples of the concepts that exist along side Magento, followed by an example of the Magento way of extending a pre-existing page using HTML with Magento’s form of MVC, and Magento’s form of templating.

No Framework

In a flat PHP file based system, your display code and business logic are all contained in the same file. This isn’t a very optimal solution as it makes maintenance and refactoring very difficult. It’s also not flexible for working within a team since the components aren’t decoupled for easy editing and may cause merging conflicts when using source code versioning systems.

<!-- Your mixed HTML+PHP page script (pseudo-code) -->
<?php mysql_connect(/* database details */) ?>
<?php $result = mysql_query(/* get current page from db */) ?>
<?php $page = mysql_fetch_assoc($result) ?>
<html>
<head>
<title><?php echo htmlentities($page['title']) ?></title>
</head>
<body>
<h1><?php echo htmlentities($page['title']) ?></h1>
<ul>
<?php $result = mysql_query(/* SQL Query */) ?>
<?php while($row = mysql_fetch_assoc($result)): ?>
<li><?php echo htmlentities($row['title']) ?></li>
<?php end ?>
</ul>
</body>
</html>

In a larger application, this would be very messy and very hard to maintain.

Model-View-Controller

In a MVC framework, the business logic is put into models and delegated to display components called templates, or view scripts, by the controller. Routing, business logic, and display code are decoupled into their own separate components. The benefits are that when you need to change some business logic, you do not need to look through a spaghetti mess of mixed HTML and PHP.

Example MVC controller class and model class

<?php // Your Post Model Class (pseudo-code)
class PostModel extends ModelAbstract
{
// model specific code here
}
<?php // Your Index Controller Class (pseudo-code)
class IndexController extends ControllerAbstract
{
public function showAction($request)
{
// call on Post model to get by ID
$post = new PostModel($request->id);
// expost post to view script
$this->view->post = $post;
}
}

Example MVC controller action template

<!-- Your controller action template (pseudo-code) -->
<html>
<head>
<title><?php echo htmlentities($this->post->title) ?></title>
</head>
<body>
<h1><?php echo htmlentities($this->post->title) ?></h1>
<p><?php echo $this->post->content ?></p>
</body>
</html>

We’re getting to a better pattern overall that allows us to decouple our code and encapsulate functionality but we’re not quite there yet with what Magento has in store.

Magento Blocks

Magento goes one step further in helping developers organize and maintain their code. In Magento, you have all the benefits described above. Furthermore, you can encapsulate class code to your display code in order to bind helpers that are specific to that embeddable template.

To demonstrate, I will guide you through the process of building a block that we can use to embed into our product page. You will need to create the following files and directories.

  • NAMESPACE: is going to the namespace you create your Twitter module in.
  • PACKAGE is the current theme package enabled on your Magento instance.
  • Directories:

    • app/code/local/NAMESPACE/Twitter/Block
    • app/code/local/NAMESPACE/Twitter/etc
    • app/design/frontend/enterprise/PACKAGE/template/catalog/product/view/twitter

    Files:

    • app/etc/modules/NAMESPACE_Twitter.xml
    • app/code/local/NAMESPACE/Twitter/etc/config.xml
    • app/code/local/NAMESPACE/Twitter/Block/Timeline.php
    • app/design/frontend/enterprise/PACKAGE/template/catalog/product/view/twitter/timeline.phtml

    Your app/etc/modules/NAMESPACE_Twitter.xml will look like this:

    <?xml version="1.0"?>
    <config>
    <modules>
    <NAMESPACE_Twitter>
    <active>true</active>
    <codePool>local</codePool>
    </NAMESPACE_Twitter>
    </modules>
    </config>

    Your app/code/local/NAMESPACE/Twitter/etc/config.xml will look like this:

    <?xml version="1.0"?>
    <config>
    <modules>
    <NAMESPACE_Twitter>
    <version>0.0.1</version>
    </NAMESPACE_Twitter>
    </modules>
    <global>
    <blocks>
    <twitter>
    <class>NAMESPACE_Twitter_Block</class>
    </twitter>
    </blocks>
    </global>
    </config>

    Your app/code/local/NAMESPACE/Twitter/Block/Timeline.php will look like this:

    <?php
    class NAMESPACE_Twitter_Block_Timeline extends Mage_Core_Block_Template
    {
    public function getTimeline($username, $limit)
    {
    $tweets = array();
    // code to get tweets here
    return $tweets;
    }
    }

    The big benefit to Magento’s design is that this block level code is not exposed to anything but timeline.phtml (embeded later) template file that is defined in local.xml below. The code for timeline.phtml is completely encapulsated in this class leaving all other variables, methods, and classes untouched.

    Next, you can add this to your app/design/frontend/enterprise/PACKAGE/layout/local.xml:

    <!-- let's add a block to our layout that defines
    where to inject our twitter widget -->
    <catalog_product_view>
    <reference name='product.info'>
    <block type='twitter/timeline' name='product.info.twitter.timeline' as='product.view.twitter.timeline' template='catalog/product/view/twitter/timeline.phtml' />
    </reference>
    </catalog_product_view>

    We’ve attached it to the catalog_product_view handle but it can be attached to as many handles as you’d like. Just make sure the matches onto a block element in that handle.

    Your app/design/frontend/enterprise/PACKAGE/templates/catalog/product/view/twitter/timeline.phtml will look like this:

    <div id='twitter-timeline'>
    <?php foreach ($this->getTimeline('demacmedia', 2) as $tweet): ?>
    <div class='tweet'><?php echo $tweet['text'] ?></div>
    <?php end ?>
    </div>

    This will define what the block will output when we embed it into our product view page.

    Finally, you can embed this line into your app/design/frontend/enterprise/PACKAGE/templates/catalog/product/view.phtml

    <?php echo $this->getChildHtml('product.info.twitter.timeline') ?>

    It seems like a lot of work but the flexibility and encapsulation gained from using such a system outweighs the negatives greatly!