=========================
The eZ Template Component
=========================
:Author: Tobias Schlitt
:Date: Monday 17 July 2006 11:50:00 am
.. contents:: Table of Contents
This article describes the template engine included in version 1.1 of eZ
components. I analyze a simple application that, using the template engine,
queries Google for specified keywords and then checks the rank of a website in
the results. While I am not going to describe the entire application (which
also uses the eZ PersistentObject component and some more classes), describing
the template aspect of the application should give you a good idea of how the
eZ Template can be used.
The models
==========
Site
----
* site: The site URI to look for in the search results.
Search
------
* query: The query string to search for on Google.
Result
------
* rank: Either the position of the site in the Google search results or 0 (if the site was not found).
* timestamp: The time when the search was performed.
* title: Either the title of the site or an empty string (if the site was not found).
* url: Either the complete URL or an empty string (if the site was not found).
* snippet: Either the text snippet Google displayed for the URL or an empty string (if the site was not found).
Application architecture
========================
The application follows the MVC pattern, similar to the one described in the
Image Gallery article (which described other aspects of the eZ components).
There is a main controller that dispatches to several action controllers based
on a URL parameter. Rather than analyzing this aspect in detail, we will look
at the portion of the main controller that interacts with the template engine.
Each action controller contains the models that are needed to fill in the
template variables and tell the main controller which template file to load.
Specifically, we will look at the action controller that displays the detailed
view of the search containing a list of the results as they are collected.
The PHP code
============
As mentioned above, we are only looking at the parts of the PHP code within the
controller that deal with the eZ Template component and the action controller.
The main controller
-------------------
The template engine is called by two of the controller methods. In the
constructor of the controller, we configure the template engine. The method
display() loads a template, submits the current action controller to it and
displays it.
Here is the interesting part of the constructor::
class tsGrcController
{
// ...
public function __construct()
{
// ...
$eztConfig = ezcTemplateConfiguration::getInstance();
$eztConfig->templatePath = dirname( __FILE__ ) . "/templates";
$eztConfig->compilePath = dirname( __FILE__ ) . "/templatesc";
}
I removed the code in this section that reads the selected action from a GET
parameter and instantiates the necessary action controller, which is then
stored in the private attribute $action. The code snippet above shows the
configuration of the Template component. The call to
ezcTemplateConfiguration::getInstance() is a singleton implementation for the
template configuration. We tell the configuration object where our templates
are stored and where compiled templates will be stored. The eZ Template
component compiles its templates into pure PHP code and caches the compiled
versions. Templates automatically get recompiled when they change. (Note that
the directory where compiled templates are stored must be writable by the user
account running the web server.)
::
public function run()
{
$this->action->run();
$template = new ezcTemplate();
$template->send->action = $this->action;
echo $template->process( $this->action->template . ".ezt" );
}
This is the run() method of the main controller. It simply calls the run method
of the action controller in the first line. (We will talk about this in greater
detail below.) After that, a new template is instantiated. The third line of
code sends the action controller to the template. Sending a variable to a
template is necessary to make it available to the template code. You can send
as many variables as you like to a template by simply assigning them to a child
of the template's $send attribute. The name you choose for the child is the
name under which the variable will be available in the template. The last line
makes the template engine process the template (which is selected by the action
controller) and prints the results. If the selected template is already
compiled, the Template component runs the compiled version. If no compiled
version is available or if the template has changed since the last compile, it
is compiled and stored.
The action controller
---------------------
Now we will look at the action controller we mentioned above::
class ActionSearchdetails
{
private $searchId;
public $template = "searchdetails";
public $title = "Search details";
public $site;
public $search;
public $results;
public function __construct()
{
// ...
}
public function run()
{
$this->search = tsGrcController::getSession()->load( 'Search', $this->searchId );
$this->results = $this->search->getResults();
$this->site = $this->search->getSite();
}
}
The constructor, which I did not include here, processes the parameters needed
by this action, specifically the ID of the search we want to display. The
retrieved search ID is stored in the private member $searchId. In the first
line of the run() method, you see how the Search object is loaded from the
database and stored in the public member called $search. After that, we call
several methods on this object to retrieve the related results and the site
this search belongs to. Both are also stored in public attributes.
Templating
==========
Now we come to the interesting part of this article. We have all the basics in
place to start looking at the template for our action.
The main template
-----------------
::
{use $action}
{var
$menu = array( "Back to site" => array( "action" => "sitedetails", "site" => $action->site-id ) )
}
{include "header.ezt" send $action, $menu}
Search details for search "{$action->search->query}" about site "{$action->site->site}"
{include "footer.ezt"}
If you are familiar with Smarty, you will find it easy to understand our
template language, since it is very similar to the Smarty language. However, in
contrast to Smarty, the eZ Template component is written only for PHP 5.1 or
greater, while Smarty maintains compatibility with PHP 4. Each template
instruction is wrapped in curly braces ("{", "}") and each of these
instructions may output text (so you do not need to specify a command like echo
for printing text). All printed text is automatically escaped using PHP's
htmlentities() function (assuming that we did not change the context of the
template, which is XHTML by default). If, for some reason, you need to print
non-escaped text, you can use the raw command.
The first template instruction is the use command, which retrieves the
variables that have been sent to the template. (Remember that we did that in
the main controller.) After that, the variable $action is available for use.
Next we define a new variable called $menu. This is a multi-dimensional
associative array that represents a menu structure. (In the context of the eZ
Template component we talk about associative arrays as hashes, so I will use
that term from now on.) As you can see, the hash is defined in exactly the same
way that it is done in PHP. The menu structure defines a link named "Back to
site". The link name is assigned to an array of parameters that needs to be
sent to the main controller to show the site we want to link to. We will show
how the menu is generated from this array in a moment.
Next we include another template that is responsible for displaying the HTML
header and the menu. Including a template works exactly like processing a
template from PHP; you have to send it the variables that it will need. Here we
send our $action and the newly generated $menu variables. But before we look at
the included template, let's finish with the main template.
In the next line (6), some HTML code gets generated. We print a first-level
heading and, for the first time, echo some data. As already mentioned, this is
quite easy - you just wrap the variable that will be output in curly braces.
Access to object attributes works the same as with PHP. Note, though, that for
now it is not possible to call methods on objects from a template. If you need
to access methods, you can try to emulate this through overloading in your
class. This is a very limited approach, but it may be sufficient in some cases.
In line 14 we see a special feature of the eZ Template component - the "cycle".
A cycle is a single-dimensional array (or a hash). It is special because you
can iterate over it in an infinite loop. When the cycle reaches the end, it
will start from the beginning again. Our cycle $rowStyle contains two elements:
the strings "light" and "dark", which refer to CSS classes. Each time you use
the cycle variable, it will return its currently selected value. To advance its
internal pointer to the next value, call the increment statement (line 27).
In line 15 we use a foreach loop to iterate over all results stored in our
action. The foreach loop is closed in line 28, using the statement::
{/foreach}
As you can see, ending blocks in the template language are similar to the HTML
construct.
Inside the foreach we use a template function for the first time. Line 18
refers to a function called date_timestamp_format(). This function works
exactly like PHP's date() function: it takes a format string to determine how
to display a date and an optional timestamp to format. If the latter is not
specified, the current time will be used. In most cases, we named the functions
in our template language differently from the PHP functions to provide a unique
naming scheme. You will also see that with parameter orders. Where PHP is
sometimes very inconsistent, the order is always the same for our functions.
Line 24 contains another function call. The function url_parameters_build()
wraps around PHP's http_build_query() function, which takes a hash and builds
an HTTP-GET query string from it. The last line in this template includes
another template, which simply closes the HTML constructs from the "
header.ezt" template we included previously. There is nothing special in there.
Included templates
==================
On line 5 of the main template we included another template that prints the
HTML header and the menu structure for each page. Remember that we sent the
$action and $menu variables to this template.::
{use $action, $menu = array()}
GoogleRankCheck - {$action->title}
{include "menu.ezt" send $menu}
Again we have a use statement on the first line. This is always necessary to
make external variables available to a template. The reason for this mechanism
is simple: in a complex system, you might have duplicate variable names, which
can be avoided via a mechanism called aliasing. Aliasing is not done in the use
statement but in the include statement. So, if you want to rename a variable
for a template you include, you can write::
{include "test.ezt" send $foo as $bar}
You can also send static values to a template using this method. In the use
statement of the header template, we define a default value for the $menu
variable. If no value is sent to the template, it will be an empty array.
We are already familiar with the rest of the code in this template, so we will
proceed to the menu template, which gets included in the last line. ::
{use $menu}
{var
$menuStd = array(
"Main page" => array( "action" => "sitelist" ),
)
}
{
$menu = array_merge( $menuStd, $menu )
}
The menu template is responsible for generating a menu on each page of our
application. The lines 2-6 define a hash of default links that should be
available on every page. After that, on line 8, the received menu hash is
merged with these default links. The array_merge() function works exactly as it
does in PHP. Lines 12-14 iterate over the resulting menu hash and generate a
table of menu items. Again we use the url_parameters_build() function on the
GET parameter array that is defined for each menu item.
Conclusion and resources
========================
The eZ Template component is a good choice if you need a template engine. It is
built exclusively for PHP 5.1 and later, making use of new and powerful PHP
functionality and usability. eZ Template has a very clean and slim API design
and supports compiling templates for the sake of performance. Because the
template language is similar to PHP's, it is easy for PHP developers to write
template code. However, eZ Template removes some of PHP's oversized features,
which makes it easy for HTML designers who might not be familiar with
programming to use. eZ Template adds value for your template engineers like the
cycle-statement, which makes tasks like generating alternating row colors
simple. The consistency in the names and prototypes of the functions provide a
very low learning curve for programming eZ templates.
Resources
=========
* `eZ components`__
__ http://ezcomponents.org