Templates

In the previous chapter, you have learned how to handle HTTP requests with Slim Framework. You should be able to create your own routes and access values entered in HTML forms by the end-user. I have also discouraged you from generating HTML pages directly within the route handlers.

In theory, generating a HTML page is nothing complicated. In practice, it gets cluttered quickly and it is prone to Cross Site Scripting attacks and other errors. The solution to this is to use templates.

The advantage of using templates is that they simplify both the PHP and HTML code and that they protect your code against Cross Site Scripting vulnerabilities. The templates disadvantage is that you need to learn another language. HTML templates are called templates, because they are HTML pages with placeholders. The actual value of the placeholders is supplied when the template is rendered (which usually means – sent to a web browser). Only then the template becomes a valid HTML page that is sent to the web client. Then it is HTML page is rendered on the end-user’s screen.

Yes, there are two renderings. First the template is rendered into an HTML page. Second the HTML page is rendered on the user screen. Each is a completely different process, but they are called the same. Here, we’ll be working with the first one.

Templating Engines

Template engine is a library which processes an HTML page with macros (snippets of a template language) and produces a valid HTML page. There are many templating engines available for PHP, some of the popular engines are: Smarty, Latte, Twig. All of them (and many others) can be used as parts of their respective frameworks or standalone. In the following examples I will stick to using the standalone Latte templating engine. The choice is rather arbitrary, as all templating engines work in a very similar way, have almost the same features and they even have a somewhat similar syntax.

Getting Started with Latte

If you followed the previous steps — if you used the prepared skeleton — I have good news for you. Latte is ready to use in your project. Actually, you should already have a route in you routes.php file similar to one below:

<?php 
$app->get('/sample', function (Request $request, Response $response, $args) {
    // Render sample view
    return $this->view->render($response, 'sample.latte');
});

So what does the line $this->view->render($response, 'sample.latte'); do ? It uses the view property of the Slim application, which happens to be a little utility class. Its most important method is render which takes a response object and a name of template file and returns a response object with a rendered HTML page. That means we can directly return the response in the handler and it will be sent to the web client. The name of the template file refers to a file in the templates directory. Go ahead and visit the /sample URL in your application. If it works, your application just used a Latte template.

Templates

In the the templates directory, you should have a file named sample.latte with contents similar to this:

{extends layout.latte}

{block title}TITLE OF PAGE{/block}

{block body}
    <div class="container">
        <h1>BODY CONTENT</h1>

        <form action="{link sample-process}" method="post">
            <label>Type your name:</label>
            <input class="form-control" type="text" name="person">
            <br>
            <input class="btn btn-primary" type="submit" value="OK">
        </form>
    </div>
{/block}

The template code is HTML document with placeholders or [macros] enclosed in curly braces {}. Everything in curly braces is Latte, everything else is plain old HTML. In the above template, there are the following pieces of Latte code:

  • {extends layout.latte} — a reference to layout template (see below)
  • {block title}...{/block} and {block body}...{/block} — code which defines contents of a template block, the block itself is defined in the layout template
  • {link sample-process} — a link macro

To understand what the layout template is and how blocks work, you have to look in layout.latte file:

<!DOCTYPE html>
<html lang="cs">
<head>
    <meta charset="utf-8">
    <title>{include title}</title>
    <link rel="stylesheet" href="css/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/bootstrap/css/bootstrap-theme.min.css">
    <script type="text/javascript" src="js/jquery.js"></script>
    <script type="text/javascript" src="css/bootstrap/js/bootstrap.min.js"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1">    
</head>
<body>
{include body}
</body>
</html>

The above is also a latte template, it contains only two pieces of Latte code {include title} and {include body}. Otherwise it is a valid HTML page (though rather empty). Both blocks of code use the include macro which defines a block of the given name (title and body) and expects some other template to fill in their contents. And that is exactly what the sample.latte template does. It takes the layout.latte template and then fills in the missing pieces for title and body.

Sounds complicated? Can’t you put all the HTML code in one template? Why bother with the blocks? The answer is laziness. Imagine that your application contains 30 pages (not that much actually). Each one of them will contain the same HTML header as you can see in the layout template (the header loads Bootstrap and JQuery libraries). Sure, you can copy it 30 times, but what if you later need to add another library in all your pages?

In short, using layout templates allows you to share common code, which in turn allows you to save hell of a time. Also it helps to reduce repeated, which allows you to concentrated only on the important stuff.

Macros

Template engine offers plenty of macros which simplify generation of the HTML code. In the above example, I used the {extends} (uses a layout template), {block} (defines a block) and {include} (includes a block) macros. However, the most important is the {$variable} macro, which allows you to safely (without the possibility of a Cross Site Scripting attack) insert parameters in the HTML code. Try adding the following route in your application:

<?php
$app->get('/variables', function (Request $request, Response $response, $args) {
	$this->view->addParam('pageTitle', 'Template engine sample');
	return $this->view->render($response, 'sample-1.latte');
});

Template file sample-1.latte:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>{$pageTitle}</title>
	</head>
	<body>
		<h1>Title: {$pageTitle}</h1>
		<p>This page is generated by Latte.</p>		
	</body>
</html>

The template expects the pageTitle variable (parameter) and puts it in the <title> and <h1> elements. The route handler must provide value for the variable, which is done using the $this->view->addParam call. The first argument of the addParam method is the name of the variable (pageTitle) and the second parameter is an arbitrary value.

Task – Simplify Template

Have you noticed that the sample-1.latte template in the above example is not using the layout template? Go ahead and modify it so that it uses it.

{extends layout.latte}

{block title}{$pageTitle}{/block}

{block body}
	<h1>Title: {$pageTitle}</h1>
	<p>This page is generated by Latte.</p>
{/block}

Advanced Macros

You can put comments in the templates as well:

{* this is a comment *}

Contrary to comments in HTML, comments in templates will not be contained in the resulting page. There are also more complicated macros:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>{$pageTitle}</title>
	</head>
	<body>
		<h1>Title: {$pageTitle}</h1>
		<h2>Flintstones</h2>
		<ul>
		{foreach $flintstones as $role => $name}
			<li>{$role|capitalize} is
				{if $showBold}
					<strong>{$name}</strong>
				{else}
					{$name}
				{/if}
			</li>
		{/foreach}
		</ul>
		<h2>Rubbles</h2>
		<ul>
			<li n:foreach="$rubbles as $role => $name">{$role|capitalize} is 
				<strong n:tag-if="$showBold">{$name}</strong>
			</li>
		</ul>
	</body>
</html>

And a corresponding route to fill in the parameters:

<?php
$app->get('/flintstones', function (Request $request, Response $response, $args) {
    $this->view->addParam('pageTitle', 'Flintstones');
    $this->view->addParam('showBold', true);
    $this->view->addParam(
        'flintstones',
        ['father' => 'Fred', 'mother' => 'Wilma', 'child' => 'Pebbles']
    );
    $this->view->addParam(
        'rubbles',
        ['father' => 'Barney', 'mother' => 'Betty', 'child' => 'Bamm-Bamm']
    );

    return $this->view->render($response, 'flintstones-1.latte');
});

There are two options how the macros can be written in latte. In the template above, the flintstones array is printed using the longer syntax {foreach}{/foreach} and {if}{/if} which is more similar to the PHP syntax. The rubbles array is printed using the shorter syntax n:foreach and n:if, which disturbs the flow of the HTML code much less. The choice is yours.

The statement {$role|capitalize} applies the built-in Latte capitalize filter. Be sure to check the manual for other useful features. In the PHP code, I need to define all the variables: $flintstones, $rubbles, $pageTitle and $showBold.

Task – Simplify Template

For the sake of practicing, go ahead and again simplify the flintstones-1.latte template so that it uses the layout template.

{extends layout.latte}

{block title}{$pageTitle}{/block}

{block body}
<h1>Title: {$pageTitle}</h1>
<h2>Flintstones</h2>
<ul>
{foreach $flintstones as $role => $name}
	<li>{$role|capitalize} is
		{if $showBold}
			<strong>{$name}</strong>
		{else}
			{$name}
		{/if}
	</li>
{/foreach}
</ul>
<h2>Rubbles</h2>
<ul>
{foreach $rubbles as $role => $name}
	<li>{$role|capitalize} is 
		{if $showBold}
			<strong>{$name}</strong>
		{else}
			{$name}
		{/if}
	</li>
{/foreach}
</ul>
{/block}

It is also possible to simplify the PHP code a little bit, if you pass all the template variables as an associative array.

<?php
$app->get('/flintstones', function (Request $request, Response $response, $args) {
    $tplVars = [
        'pageTitle' => 'Flintstones',
        'showBold' => true,
            'flintstones' => [
            'father' => 'Fred',
            'mother' => 'Wilma',
            'child' => 'Pebbles',
        ]
	];
	$tplVars['rubbles'] = [
        'father' => 'Barney',
        'mother' => 'Betty',
        'child' => 'Bamm-Bamm',
    ];
	$this->view->addParams($tplVars);
	return $this->view->render($response, 'flintstones-2.latte');
});

In the PHP code, I need to define all the variables: $flintstones, $rubbles, $pageTitle and $showBold in an associative array. I have taken the liberty to shorten the name of the variable $templateVariables to just $tplVars.

If you find passing variables between a PHP script and a template confusing, have a look at the following schema.

Schematic of template variables

Task – Contact form

Let’s convert the contact form we did in one of the earlier chapter into a template.

<?php
$app->get('/contact-form', function (Request $request, Response $response, $args) {

    $currentUser = [
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => 'john.doe@example.com',
        'birth_year' => 1996,
    ];
    /*
    // Not Logged User
    $currentUser = [
        'first_name' => '',
        'last_name' => '',
        'email' => '',
        'birth_year' => '',
    ];
    */
    if ($currentUser['first_name']) {
        $tplVars['message'] = "Hello,\nI'd like to know more about your product <ProductName>\n\nBest Regards,\n" .
            $currentUser['first_name'] . ' ' . $currentUser['last_name'];
    } else {
        $tplVars['message'] = "Hello,\nI'd like to know more about your product <ProductName>\n\nBest Regards,\n<YourName>";
    }

    $tplVars['rows'] = 10;
    $tplVars['cols'] = 50;
    $tplVars['pageTitle'] = "Contact form";
    $tplVars['currentUser'] = $currentUser;
    $tplVars['years'] = [];
    for ($year = 1916; $year < date('Y'); $year++) {
        $tplVars['years'][] = $year;
    }
    $this->view->addParams($tplVars);
    return $this->view->render($response, 'contact-form.latte');
});

Template file form-5.latte:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>{$pageTitle}</title>
    </head>
    <body>
        <h1>{$pageTitle}</h1>
        <h2 n:if="$currentUser['first_name']">Hello {$currentUser['first_name']} {$currentUser['last_name']}</h2>
    	<form method="post" action="http://odinuv.cz/form_test.php">
    		<ul>
    			<li>
    				<label>First name:
    					<input type="text" name="first_name" value="{$currentUser['first_name']}">
    				</label>
    			</li>
                <li>
                    <label>Last name:
                        <input type="text" name="last_name" value="{$currentUser['last_name']}">
                    </label>
                </li>
                <li>
                    <label>E-mail address:
                        <input type="email" name="email" value="{$currentUser['email']}">
                    </label>
                </li>
                <li>
                    <label>Year of birth:
                        <select name="birth_year">
                        {foreach $years as $year}
                            <option n:attr="selected => $currentUser['birth_year'] == $year" value="{$year}">{$year}</option>
                        {/foreach}
                        </select>
                    </label>
                </li>
    			<li>
    				<label>Message for us:
    					<textarea name="message" cols="{$cols}" rows="{$rows}">{$message}</textarea>
    				</label>
    			</li>
    		</ul>
    		<button type="submit" name="contact" value="contact">Contact Us</button>
    	</form>
	</body>
</html>

I have simplified the condition if ($templateVariables['currentUser']['firstName'] != '') to if ($templateVariables['currentUser']['firstName']) because the automatic boolean conversion allows us to do it.

You need to convert the entities &lt; and &gt; in the message back to the characters < and >. Now Latte does this conversion automatically for you.

Task – Person Form

Using templates, create a form like the one below. Assume that you have a variable $person which contains the default values for the form inputs. The person variable should be an associative array with the keys id, first_name, last_name, nickname, birth_day, height.

<?php
// This is not a solution. It is only a hint, what the PHP script should contain
// Existing user
$person = [
    'id' => 123,
    'first_name' => 'John',
    'last_name' => 'Doe',
    'nickname' => 'johnd',
    'birth_day' => '1996-01-23',
    'height' => 173,
];
/*
// New user
$person = [
    'id' => null
    'first_name' => '',
    'last_name' => '',
    'nickname' => '',
    'birth_day' => null,
    'height' => null,
];
*/

Wondering about the route name? add-person or person-add are good URLs.

<?php
$app->get('/person-add', function (Request $request, Response $response, $args) {
    // Existing user
    $person = [
        'id' => 123,
        'first_name' => 'John',
        'last_name' => 'Doe',
        'nickname' => 'johnd',
        'birth_day' => '1996-01-23',
        'height' => 173,
    ];
    /*
    // New user
    $person = [
        'id' => null
        'first_name' => '',
        'last_name' => '',
        'nickname' => '',
        'birth_day' => null,
        'height' => null,
    ];
    */
    if ($person['id']) {
        $tplVars['pageTitle'] = "Edit person";
    } else {
        $tplVars['pageTitle'] = "Add new person";
    }
    $tplVars['person'] = $person;
    $this->view->addParams($tplVars);
    return $this->view->render($response, 'person-form.latte');
});

Template file person-form.latte:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>{$pageTitle}</title>
    </head>
    <body>
        <h1>{$pageTitle}</h1>
        {if $person['id']}
            <h2>Update person {$person['first_name']} {$person['last_name']}</h2>
        {else}
            <h2>Create a new person</h2>
        {/if}
    	<form method="post" action="http://odinuv.cz/form_test.php">
    		<ul>
    			<li>
    				<label>First name:
    					<input type="text" name="first_name" value="{$person['first_name']}">
    				</label>
    			</li>
                <li>
                    <label>Last name:
                        <input type="text" name="last_name" value="{$person['last_name']}">
                    </label>
                </li>
                <li>
                    <label>Nickname:
                        <input type="text" name="nickname" value="{$person['nickname']}">
                    </label>
                </li>
                <li>
                    <label>Date of Birth:
                        <input type="date" name="birth_day" value="{$person['birth_day']}">
                    </label>
                </li>
    			<li><label>Height:
                        <input type="number" name="height" value="{$person['height']}">
                    </label>
    			</li>
    		</ul>
            {if $person['id']}
                <button type="submit" name="submit" value="update">Update</button>
            {else}
                <button type="submit" name="submit" value="create">Create new</button>
            {/if}
    	</form>
	</body>
</html>

Summary

Using a template engine requires you to learn its macro language. However it does lead to a cleaner and safer HTML and PHP code. You don’t need to struggle so much with using proper quotes. When using templates, don’t forget that the variables defined inside a template are only those passed via the addParam or addParams methods! Variables from the PHP script are not available in the template automatically. Also keep in mind that while a HTML Template is very similar to a HTML page, it cannot be interpreted by the web browser, only the corresponding template engine is capable of processing it and producing a valid HTML page.

Now you should be familiar with the principle of a PHP template engine and you should be aware of the benefits of using a template engine. You should be able to use basic macros for inserting variables in a template and working with conditionals and loops in Latte templates (either syntax).

New Concepts and Terms

  • Templates
  • Latte
  • Macros
  • Template Variables