4.4. Handlebars
PHP Handlebars matches the JavaScript Version and has compile time helper support and super nice compile time error reporting. This version of Handlebars is based on caching the compiled templates and inherently made the overall compile times faster. Loading at ~50ms uncached and ~30ms cached.
Let’s create a new file in the /public
directory called templating.php
and
paste the following code in public/templating.php
.
Figure 4.4.1.A. Basic Handlebars
require_once '../vendor/autoload.php';
use Cradle\Handlebars\HandlebarsHandler as Handlebars;
//load up handlebars instance
$handlebars = new Handlebars();
//compile the template
$template = $handlebars->compile('<h1>{{article_title}}</h1><p>{{article_detail}}</p>');
//bind the values to the template and echo out
echo $template([
'article_title' => 'Article 1',
'article_detail' => 'A story about Article 1...'
]);
After running http://127.0.0.1:8888/templating.php
, you should see the
article’s output. Let’s go the Front End’s static controller and
create a playful route like the following code in /app/www/src/controller/static.php
.
Figure 4.4.1.B. Handlebars in a Route
...
$this->get('/handlebars/is/fun', function($req, $res) {
$global = $this->package('global');
//load up handlebars instance
$handlebars = $global->handlebars();
//compile the template
$template = $handlebars->compile('<h1>{{article_title}}</h1><p>{{article_detail}}</p>');
//bind the values to the template and echo out
$page = $template([
'article_title' => 'Article 1',
'article_detail' => 'A story about Article 1...'
]);
$res->setContent($page)
});
...
In the JavaScript Version of
Handlebars, we are merely following the guidelines which do not handle files
explicitly. Instead we create wrapper functions to do this in
/bootstrap/handlebars.php
.
Figure 4.4.1.C. Global Handlebars Methods
...
/**
* Returns the global handlebars object
*
* @return Handlebars
*/
->addMethod('handlebars', function () {
static $handlebars = null;
if (is_null($handlebars)) {
$handlebars = HandlebarsHandler::i();
}
return $handlebars;
})
/**
* Makes a rendered template
*
* @return string
*/
->addMethod('template', function ($file, array $data = [], array $partials = []) {
if (!file_exists($file)) {
return null;
}
$template = file_get_contents($file);
$handlebars = cradle('global')->handlebars();
foreach ($partials as $name => $content) {
if (file_exists($content)) {
$content = file_get_contents($content);
}
$handlebars->registerPartial($name, $content);
}
$compiled = $handlebars->compile($template);
return $compiled($data);
});
...
There are two methods here, one called handlebars()
which returns the
Handlebars singleton instance and one called template()
that accepts a
template file path (amongst other things).
Let’s create a template in /app/www/src/template/handlebars/template.html
with
the following code.
Figure 4.4.1.C. Basic Handlebars Template File
<h1>{{article_title}}</h1>
<p>{{article_detail}}</p>
<h3>Comments</h3>
<hr />
<h4>Reply to: {{article_detail}}</h4>
{{#if comment.0.comment_thumbs}}
<em>+1</em>
{{else}}
<em>-1</em>
{{/if}}
<p>{{comment.0.comment_detail}}</p>
<hr />
<h4>Reply to: {{article_detail}}</h4>
{{#if comment.1.comment_thumbs}}
<em>+1</em>
{{else}}
<em>-1</em>
{{/if}}
<p>{{comment.1.comment_detail}}</p>
In the above code, we are trying to set up an output for an article page. We can
access multi-dimensional arrays using the dot notation. For example below
the following $data
if passed to Handlebars can access comments like
{{comment.0.comment_detail}}
.
$data = [
'article_title' => 'Article 1',
'article_detail' => 'A story about Article 1...',
'comment' => [
[
'comment_thumbs' => 1,
'comment_detail' => 'That was beautiful.'
],
[
'comment_thumbs' => 0,
'comment_detail' => 'Um... I dont get it.'
]
],
];
Handlebars also has control statements like
{{#if comment.0.comment_thumbs}}
, but their control
statements cannot compare values.
This is why we created a custom helper called `when` to solve for comparing
values in a control statement.
Let’s now connect this template file to our fun route in
/app/www/src/controller/static.php
.
Figure 4.4.1.D. Connecting the Template
...
$this->get('/handlebars/is/fun', function($req, $res) {
$global = $this->package('global');
//load up handlebars instance
$handlebars = $global->handlebars();
//determine the template file
$file = dirname(__DIR__) . '/template/handlebars/template.html';
//determine the data
$data = [
'article_title' => 'Article 1',
'article_detail' => 'A story about Article 1...',
'comment' => [
[
'comment_thumbs' => 1,
'comment_detail' => 'That was beautiful.'
],
[
'comment_thumbs' => 0,
'comment_detail' => 'Um... I dont get it.'
]
],
];
//compile the template
$page = $global->template($file, $data);
//set the content
$res->setContent($page)
});
...
Handlebars also has looping out of the box and as per their
JavaScript Version documentation.
In /app/www/src/template/handlebars/template.html
, let’s use this loop
statement for the comment section in the next example below.
Figure 4.4.1.E. The each
Helper
<h1>{{article_title}}</h1>
<p>{{article_detail}}</p>
<h3>Comments</h3>
{{#each comment}}
<hr />
<h4>Reply to: {{../article_detail}}</h4>
{{#if comment_thumbs}}
<em>+1</em>
{{else}}
<em>-1</em>
{{/if}}
<p>{{comment_detail}}</p>
{{/each}}
This template also exposes how Handlebars navigates through the original data
set passed to the template via {{../article_detail}}
.
Since that code is in the each
loop, it accesses the parent variable by
pre-pending a ../
. Handlebars can also work with multiple templates at the
same time. When a template is called inside of another template it is called a
partial. In /app/www/src/template/handlebars/template.html
, let’s move the
{{#if comment_thumbs}}
control statement to a partial.
Figure 4.4.1.F. Adding a Partial
<h1>{{article_title}}</h1>
<p>{{article_detail}}</p>
<h3>Comments</h3>
{{#each comment}}
<hr />
<h4>Reply to: {{../article_detail}}</h4>
{{> thumbs}}
<p>{{comment_detail}}</p>
{{/each}}
Next let’s create the partial file /app/www/src/template/handlebars/thumbs.html
,
and paste in the code for comment_thumbs
like the following example
Figure 4.4.1.G. Thumbs Partial File
{{#if comment_thumbs}}
<em>+1</em>
{{else}}
<em>-1</em>
{{/if}}
Lastly let’s register this partial in Handlebars using our bootstrap helper as in the following example.
Figure 4.4.1.H. /app/www/src/controller/static.php
...
$this->get('/handlebars/is/fun', function($req, $res) {
$global = $this->package('global');
//load up handlebars instance
$handlebars = $global->handlebars();
//determine the template file
$file = dirname(__DIR__) . '/template/handlebars/template.html';
//determine the data
$data = [
'article_title' => 'Article 1',
'article_detail' => 'A story about Article 1...',
'comment' => [
[
'comment_thumbs' => 1,
'comment_detail' => 'That was beautiful.'
],
[
'comment_thumbs' => 0,
'comment_detail' => 'Um... I dont get it.'
]
],
];
//add partials
$partials = [
'thumbs' => dirname(__DIR__) . '/template/handlebars/thumbs.html'
];
//compile the template
$page = $global->template($file, $data, $partials);
//set the content
$res->setContent($page)
});
...
After running http://127.0.0.1:8888/templating.php
, you should see the final
article’s output using partials. That’s it for the Handlebars intro, but in
order to fully utilize its potential on the system you should also go over the
following chapters and references.