Calypso DTL - Tutorial


Installation

Download the latest version of the Calypso DTL. Unpack it into a directory of your project or your PHP/PEAR include directory. As of now Calypso can only be powered when utilizing PHPs Autoload functionality. That means, at the beginning of your php script using the Calypso DTL you have to extend the include path of PHP to be aware of Calypso DTL:

<?php
set_include_path(get_include_path().":/path/to/calypso/library_folder");
?>

Now we have to utilize the Autoload functionality of PHP5 based on the question if we are using Calypso DTL with Zend Framework or standalone.

Zend Framework

Zend Frameworks Zend_Loader is include path aware. You can easily enable Calypso DTL usage by calling:

<?php
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
?>

The chances are good you have already called this to use the Zend Framework, so the only thing to get Calypso DTL running with the Zend Framework is, putting it into the Include Path. The next thing is to make the MVC compononents aware of the fact, that they should use Calypso DTL. You have to modify your bootstrap before the Front Controller dispatching and tell the ViewRenderer Helper to use Calypso DTL as the template engine choice.

$application_path = "path/to/your/apps/folder";
$front = Zend_Controller_Front::getInstance();

$view = new Calypso_View_Dtl();

$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view)
             ->setViewBasePathSpec($application_path."/modules/:module/views")
             ->setViewSuffix('tpl');

Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

$front->dispatch();

You're finished, Zend Framework now uses the Calypso DTL as Template/View Engine for all pages of the project. If youre using Zend Framework without MVC you can still use Calypso DTL:

$view = new Calypso_View_Dtl();
$view->setScriptPath("/path/to/view/scripts");

$view->assign('var', $var);
$view->variable2 = $array;

echo $view->render('page.tpl');

Standalone

Going the Standalone way is a bit more complicated, since we cannot utilize the Zend Loaders ability to Autoload all our dependancies. We have to write or own function to handle this:

<?php
class CalypsoLoader
{
    public static function autoload($class)
    {
        $file = str_replace("_", "/", $class).".php";
        @include_once($file);
    }
}

spl_autoload_register(array('CalypsoLoader', 'autoload'));

This dynamically loads all classes that Calypso needs from the include path correctly. Now you can get started with Calypso DTL:

$template = new Calypso_View_Dtl_Template("/path/to/template/scripts/");
// Context Implements ArrayAccess with an extension for multidimensional arrays
// http://de.php.net/manual/en/class.arrayobject.php
$context = new Calypso_View_Dtl_Context();
$context["var"] = $var;
$context["fruits"] = array('apple', 'banana', 'orange');

echo $template->render("template.tpl", $context);
                    

This is all slightly more complicated than with using the Zend Framework. In the future a more easy usage might be possible. by implementing a Wrapper around the DTL Context and Template Objects much as Calypso_View_Dtl is for the Zend Framework.


A 'Hello World' Zend Framework MVC Project with Calypso DTL

If you have followed the Zend Framework installation for MVC using the ViewRenderer you are already setup to use Calypso DTL as your template engine in the Zend Framework. In your project directory structure you now have to create controller/action.tpl template scripts in the "/application/view/scripts" folder.

Usage inside the controller is then exactly the same as if you would use the Zend_View implementation for your projects:

class FooController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $rows = array();
        // Injected into Calypso DTL Context here:
        $this->view->assign('rows', $rows);
    }
}

Calling this controller now, for example: http://example.com/foo/index results in the error that "foo/index.tpl" is missing. We give in to the framework and create the file in "application/views/scripts/foo/index.tpl":

<html>
    <head>

        <title>My Calypso DTL example</title>
    </head>

    <body>
        Hello World
    </body>
</html>

This now renders but has no single DTL statement in it. We seperate the root layout from the foo/index template by using template inheritence, we create a layout.tpl file.

<html>
    <head>
        <title>{% block title %}My Calypso DTL example{% endblock %}</title>
    </head>

    <ul>
        <li>Navigation 1</li>
        <li>Navigation 2</li>
    </ul>

    <body>
        {% block content %}{% endblock %}
    </body>
</html>

And modifiy our foo/index.tpl:

{% extends "layout.tpl" %}

{% block title %}My Calypso DTL example - Foo/index{% endblock %}

{% block content %}

    {% for row in rows %}
  • {% row %} {% endfor %}
{% endblock %}

Voila!


Client Site Rendering with DojoX DTL and Calypso DTL Helper

Usage of the Client Side rendering capabilities requires to implement the Dojo and DojoX Headers into your HTML Head of the page that is to be rendered client site:

<script type="text/javascript" src="/path/to/dojo-release-1.1.1/dojo/dojo.js"></script>

<script type="text/javascript"><!--
dojo.require("dojox.dtl");
dojo.require("dojox.dtl.Context");
dojo.require("dojox.dtl.ext-dojo.NodeList");
--></script>

You can now use a ServerSide DTL Tag called {% ajax %} to make sure the client renders this part.

<ul id="tags">
    {% ajax "#tags" "DtlJson.php" %}
        {% for tag in tags %}
            <li><a href="/tag/{{ tag }}">{{ tag|CapFirst }}</a></li>
        {% endfor %}
    {% endajax %}
</ul>

This works as follows: When the Server Side Parser gets to the {% ajax %} tag he retrieves all tokens until {% endajax %} and puts them onto a Dtl ClientSide Template Stack together with the given information "csshook" and "url" (In our example: #tags and DtlJson.php). You can retrieve the contents of this stack by issuing the following command at the near end of your template script (at least behind all {%ajax%} tags).

{% dtlstack %}

In this case all templates on the stack are built into their javascript code. The client side dtl rendering injects the generated template with json data from the given url (in this case: DtlJson.php) into the innerHtml of the Node given as "csshook" parameter (in this case #tags). The result will look like this:

<script language="javascript" type="text/javascript"><!--
dojo.addOnLoad(function(){
    dojo.xhrGet({url: "DtlJson.php", handleAs: "json", handle: function(data,ioArgs){ 
        if(typeof data != "error"){
            dojo.query("#tags").dtl(
                "{% for tag in tags %} <li><a href=\"/tag/ {{ tag }} \">{{ tag|CapFirst }}</a></li>{% endfor %}", data); 
            }
        }
    });
});
--></script>

And from DtlJson.php we get the following information:

<?php
    $tags = array();
    for($i = 0; $i < 1000; $i++) {
        $tags[] = "Tag".$i;
    }

    header('Content-type: text/json');
    echo json_encode(array('tags' => $tags));
?>

What do we get in the end? 1000 tags with names "Tag0", "Tag1", .... will be generated in JSON format, retrieved from the DtlJson.php and rendered by the Client Side DojoX DTL Engine as List elements into the Unordered List with id #tags. What is the advantage? Rendering 1000 elements on the server side takes time that the user is waiting for the response. Its an user improvement that the basic page is completly rendered and the missing information is retrieved a little second later. This equally applies to table rows or other iterated content blocks (blog comments, statistics enumerations, thumbnail overviews).