Practical PHP Patterns: Template View
Join the DZone community and get the full member experience.
Join For FreeIn this series we have seen two Web presentation patterns so far:
- Page Controller, also known as Action Controller, which responds to a specific HTTP request from a client.
- Front Controller, which acts as the single entry point of a web application and routes requests to different Page Controllers.
These two patterns are part of the Controller component of an MVC implementation. But do controllers generate the HTML response directly? The response is, usually, no. Controllers are mostly glue code which deals with orthogonal concerns such as authentication, not a mean to generate a response.
In the MVC pattern, the Model is application-specific; the Controller layer is a combination of the two patterns treated earlier, while a View component is needed to complete the picture. There are different levels of complexity of the various View patterns available in the field of the web presentation, but we'll start with the classic Template View.
A Template View is a bunch of HTML code with dynamic points submerged in it; again, PHP files can be thought as an instance of this pattern.
<html>
<head>
<title>A simple Hello World script as an example of Template View</title>
</head>
<body>
<?php
echo "<p>Hello, {$_GET['name']}.</p>";
?>
</body>
</html>
Template View as an MVC component
In a web MVC approach, the data is not pulled into the view script by accessing directly the Model, because the Controller layer should be responsible for that. Nor the view script contains the behavior of the application like in the example above.
Instead, the non-static data used in the Template View is passed to it by the Controller.
As a result, the View has no dependencies towards any other classes than the ones of the object passed to it (plain data structures or Model classes such as Entities and Value Objects).
Ideally, a Template View should be declarative. It is usually implemented as a PHP script, so we have various options to render data, which ranges from the almost-declarative <?= to the programmatic one. The differences are in the noise introduced in the HTML-PHP mix and in the freedom of what can be printed.
An almost declarative mark: <?=$variable?>
A more complex statement: <?php echo $variable; ?>
A versatile approach: <?php echo $object->method(); ?>
PHP and other server-side programming languages originally started using the < and > tags because WYSIWYG editors would ignore them, and let a designer access the Template View for editing without messing up the code. There is also the problem of well-formed XML documents, which these scripts are not (though it is not usually a problem unless you want to generate them.)
PHP as a templating language even offers some alternative construct you may use in Template Views to simplify the structure of the code (bracket-less constructs):
<?php foreach ($list as $element): ?>
<a href="<?=$element?>">
<?=$element?>
</a>
<?php endforeach; ?>
Popularity
The Template View pattern is in the foundation of dynamic web pages.
PHP is born with these ideas (the recursive acronym PHP means PHP: Hypertext Preprocessor.) JSP and ASP are also similar solutions where the dynamic pieces are put into an HTML context. The problem with all these three languages when used is the quantity of business logic that get transported in the views, while we would want to separate it in a testable, reusable object model.
Smarty and some other PHP template engines take the Template View a level further, by defining a syntax for markers and parse the Template Views instead of directly executing them (thus this views are not .php files but are in some other format like .tpl). Personally I never used them since I think PHP is already a template language, which has all the flexibility I would want from one; at the same time, template engines eventually replicates the PHP syntax:
{if $edit_flag}
<input type=checkbox name=edit value="y"> edit <br>
{/if}
Advantages and disadvantages
The radical advantage of adopting a Template View pattern is the clean separation of presentation logic from business-related one. This results also in the freedom of HTML modelling from people with only basic knowledge of PHP (see designers, SEO gurus, etc.)
There are also some issues in using Template Views, but they are common to all View-related patterns. Basically, the presentation logic contained in views can be taken too further, to the point that domain-related logic scatters into the view instead of sitting in the Controller or in the Model. This is a problem which happened very often in legacy PHP applications, built before the current framework generation.
Examples
Zend Framework, our reference for Web presentation patterns, defines a generic View class which takes the path of a view script as an input, and renders it into a scope internal to one of its methods (automatically: once the controller is finished, the view is found and run.) This way, the context variables set up by the controller are made available to the view script on $this via __get and __set overloading.
<p>Hello, <?php echo $this->name; ?>.</p>
Not only variables (scalar and objects) can be put on $this, but also View helpers via __call() overloading. In this picture, custom methods can factor out presentation logic on external plugin objects which are wired to the generic View object, which forwards calls to them or returns them to view script itself.
<?php
$this->headTitle($this->object);
$this->headTitle($this->methodName);
if ($this->result) {
echo "<p>Result: $this->result</p>";
}
if ($this->form) {
echo $this->form;
}
Zend Framework also switches automatically the name of the view to render basing on some conventional parameters like format. For example, ?format=xml will cause a comments.xml.phtml to be rendered instead of comments.phtml:
<?php
if (isset($_SERVER['HTTP_HOST'])) {
$host = 'http://' . $_SERVER['HTTP_HOST'];
} else {
$host = 'http://localhost';
}
$entries = array();
foreach ($this->comments as $c) {
$guid = $host . $this->url(array('slug' => $this->article['slug']), 'article', true) . '#comment_' . $c['id'];
$entries[] = array(
'title' => 'Comment #' . $c['id'],
'link' => $guid,
'guid' => $guid,
'description' => strip_tags($c['text'])
);
}
$feed = Zend_Feed::importArray(array(
'title' => $this->article['title'],
'description' => $this->article['description'],
'author' => $this->article['author'],
'link' => $host . $this->url() . '?format=xml',
'charset' => 'utf-8',
'entries' => $entries
));
echo $feed->saveXml();
Note that all the data resources needed by the view are accessed through $this (they have to be previously injected), the View object.
Opinions expressed by DZone contributors are their own.
Comments