In part one of this two-part series I started looking at FigDice, a PHP templating system that takes a slightly different approach to most.
So far we’ve put together a very simple example website using Figdice. We’ve implemented a couple of pages, a Twitter feed and some template partials.
In this second and final part we’re going to add a simple blog to our example site, which allows us to look in more detail at Figdice’s concept of data feeds. We’ll also look at internationalization, translating some of the site’s content into a couple of additional languages.
The Code
I’ve created a separate repository for the code for this second part of the series, which you’ll find on Github. It expands upon the code we wrote in Part One.
There’s also an online demo.
Building a Simple Blog
Now let’s create a more complex example of a data feed, by implementing a simple blog.
First, create another feed class – this time for a blog.
<?php namespace Sitepoint\Feed;
use figdice\Feed;
class BlogFeed extends Feed
{
public function run() {
return array(
array(
'id' => 3,
'slug' => 'post-three',
'title' => 'Sample Blog Post Three',
'body' => '<p>Donec sed odio dui. Maecenas sed diam eget risus varius blandit sit amet non magna. Aenean lacinia bibendum nulla sed consectetur. Vestibulum id ligula porta felis euismod semper. Cras mattis consectetur purus sit amet fermentum. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>',
'author' => array(
'id' => 1,
'name' => 'Bob',
),
'created' => 12345,
),
// .. more posts here, omitted for brevity
);
}
}
Again, we’re faking the data, but retrieving blog posts from sort of storage is the sort of thing which is covered extensively elsewhere and ought to be relatively straightforward.
Now modify the FeedFactory
class, letting it know about our new feed:
// ...
use Sitepoint\Feed\BlogFeed;
class FeedFactory implements \figdice\FeedFactory
{
// ...
public function create($className, array $attributes) {
if ($className == 'BlogFeed') {
return new BlogFeed();
}
if ($className == 'TwitterFeed') {
return new TwitterFeed();
}
// ...
}
}
Now create a new view, which we’ll use for our blog listing:
<!-- file: blog.html -->
<?xml version="1.0" encoding="utf-8"?>
<fig:template xmlns:fig="http://figdice.org/">
<fig:dictionary file="menu.xml" name="menu" />
<fig:feed class="BlogFeed" target="posts" />
<fig:include file="layout.html" />
<title fig:plug="docTitle"><fig:trans dict="menu" key="blog" /></title>
<h1 fig:plug="pageTitle"><fig:trans dict="menu" key="blog" /></h1>
<div fig:plug="pageContent">
<article fig:walk="posts">
<h2>
<a href="/blog/post/{slug}" fig:text="title"></a>
</h2>
<p class="author">By <span fig:text="author/name" /></p>
<div fig:text="body"></div>
<hr fig:auto="true" />
</article>
</div>
</fig:template>
Much of this we’re already seen, but let’s look at the fig:walk
bit. This time, instead of creating a simple list element and injecting a single property, we’re creating additional markup; in this case, for each post we’re creating an <article>
element.
Once we’re in a “walk” loop – which is essentially an iterator – properties can be accessed by name; see the <div fig:text="body"></body>
for example.
Within the <h2>
tag, we create a link by using fig:text
to insert the title of the blog post inside the <a>
tag, and use curly brackets {slug}
to incorporate the post’s slug into the URL (href) attribute.
The link within the <h2>
tag is slightly more complicated, because we’re defining an expression which will get evaluated. It concatenates the string /blog/post/
with the value of the post’s slug
attribute to generate a URL, e.g. /blog/post/post-three
.
You can also access deeper properties using slashes. For the author information we want to extract the name
property from the author
; so we use <span fig:text="author/name" />
. You can also use the double-dot (..
) notation to go up a level.
<fig:dictionary>
and <fig:trans>
are used for internationalization, which we’ll look at shortly.
Feeds and Attributes
You might remember there was a second, optional parameter to the factory’s create
method. If you add any addtional attributes to the fig:feed
element that aren’t in the fig
namespace, class
or target
, they get passed to the factory as that second attribute.
For example, you could modify blog.html
as follows:
<fig:feed class="BlogFeed" target="posts" num-posts="5" sort="'date'" sort-direction="'asc'" />
Note the single quotes inside the double-quotes; this is because the values are evaluated.
Then, in your factory, you can use getParameter()
– or if you know what type to expect, getParameterBool()
/ getParameterInt()
/ getParameterString()
. Each of these methods takes a default as an optional second parameter.
So for this example, we could extend the factory like this:
// @file FeedFactory.php
public function create($className, array $attributes) {
$num_posts = $this->getParameterInt('num-posts', 10);
$sort_by = $this->getParameterString('sort', 'date');
$sort_dir = $this->getParameterString('sort-direction', 'desc');
// now you can use these values, for example when you instantiate the feed
See the feeds section of the manual for more details.
Wrapping up the Blog
Now that we’ve implemented a simple blog listing, we need to create a page to display an individual blog post.
First, let’s define the route:
// Individual blog posts.
$app->get('/blog/post/{slug}', function($slug) use ($view) {
$view->loadFile( '../templates/post.html' );
// We use mount to "inject" the slug into the view, which it can then use to "pull" the appropriate post.
$view->mount('slug', $slug);
return $view->render();
});
Notice how we’re injecting the slug – which is a URL parameter – into the view using the mount()
method.
Now let’s create the view:
// post.html
<xml fig:mute="true"> <!-- Mute because this tag should not end up in the HTML document. But the FigDice template must have an XML root node. -->
<fig:dictionary file="menu.xml" name="menu" />
<!-- Load the page layout -->
<fig:include file="layout.html" />
<fig:feed class="BlogFeed" target="post" post-slug=" /slug " />
<!-- Set the <title> tag -->
<title fig:plug="docTitle"><fig:trans dict="menu" key="about" /></title>
<!-- Set the <h1> tag -->
<h1 fig:plug="pageTitle" fig:text="/post/title" />
<!-- "Plug in" the page content -->
<div fig:plug="pageContent">
<p class="back"><a href="/blog">« <fig:trans dict="menu" key="blog" /></a></p>
<p class="author">By <span fig:text="/post/author/name" /></p>
<div fig:text="/post/body"></div>
</div>
</xml>
Much of this should be familiar, but let’s look at the <fig:feed>
element more closely:
<fig:feed class="BlogFeed" target="post" post-slug=" /slug " />
Notice how we’re feeding the “slug” variable, which we’ve made available to the view using the mount()
method, back to the BlogFeed class by using the post-slug
attribute.
Let’s modify the BlogFeed class to retrieve a single blog post. Because we’re defining posts as a simple array, place this after we instantiate it:
$posts = array( .... );
// Get the slug, if provided
$slug = $this->getParameterString('post-slug');
if ($slug) {
// Use a little Underscore magic to retrieve the appropriate post
$post = Arrays::find($posts, function($post) use ($slug) {
return ($post['slug'] == $slug);
});
return $post;
} else {
// No slug, we want the lot.
return $posts;
}
Obviously in a “real” application you’d do some sort of database lookup, but this example should give you an idea of how to set up a data feed for your views.
Conditions
A quick note on conditionals, which you can achieve using the fig:cond
attribute.
For example, to show some content given a certain condition:
<span fig:cond="logged_in == true">You are logged in</span>
To add a class to an <article>
if it’s published:
<article>
<fig:attr fig:cond="status == 1" name="class">published</fig:attr>
...
</article>
If status is set to one, the resulting HTML will be as follows:
<article class="published">
...
</article>
Internationalization
FigDice makes translating text in your templates easy. Let’s start by creating some language files to hold our translatable strings. In a large application you might want to create a file per module, making it easy to re-use them across multiple applications.
A typical structure might look like this:
lang
en
menu.xml
products.xml
checkout.xml
es
menu.xml
products.xml
checkout.xml
fr
menu.xml
products.xml
checkout.xml
Let’s create a concrete example, by translating our example site’s menu:
<!-- lang/en/menu.xml -->
<fig:dictionary xmlns:fig="http://www.figdice.org/" language="en">
<entry key="home">Home</entry>
<entry key="about">About</entry>
<entry key="blog">Blog</entry>
</fig:dictionary>
Then, the Spanish version of this file:
<!-- lang/es/menu.xml -->
<fig:dictionary xmlns:fig="http://www.figdice.org/" language="es">
<entry key="home">Inicio</entry>
<entry key="about">Acerca</entry>
<entry key="blog">Blog</entry>
</fig:dictionary>
When you want to use translated text, first reference the dictionary file and give it an identifier somewhere in your template:
<fig:dictionary file="menu.xml" name="menu" />
Now you can insert the value of an item using the dictionary’s key – taken from the name
attribute in the fig:dictionary
declaration – and the text item’s key
:
// file: templates/menu.html
<ul class="nav nav-pills pull-right">
<li><a href="/"><fig:trans dict="menu" key="home" /></a></li>
<li><a href="/about"><fig:trans dict="menu" key="about" /></a></li>
<li><a href="/blog"><fig:trans dict="menu" key="blog" /></a></li>
</ul>
Now, somewhere in your application code, tell the view where to find your language files:
$view->setTranslationPath('../lang');
Then set the language:
$view->setLanguage('es');
// or $view->setLanguage('en'), $view->setLanguage('fr'), etc
Let’s implement language-switching in our sample application, albeit keeping it really simple.
First, we’ll add some links in the header of the layout (templates/layout.html
):
<a href="/lang/en"><img src="/img/en.png" width="16" height="16" /></a> | <a href="/lang/es"><img src="/img/es.png" width="16" height="16" /></a>
You’ll find the flag icons we’re using here in the sample repository.
Now implement the corresponding route, which simply pops the appropriate language code in a session variable:
$app->get('/lang/{lang}', function ($lang) use ($app) {
$app['session']->set('lang', $lang);
return $app->redirect('/');
});
Finally, near the top of index.php
:
// Get the language from the session, if it's set - default to English.
$language = $app['session']->get('lang', 'en');
// ...and set it.
$view->setLanguage($language);
Now if you browse the site, you should find that clicking one of the flags will switch the menu labels to the appropriate language.
Summary
In these two articles I’ve introduced FigDice, a templating system which takes a very different approach to most. There’s more to it than I’ve been able to cover, so check out the manual, tutorials, examples or reference.
I expect that while some people will like the approach that FigDice takes, equally some won’t – so I’d be interested to hear your thoughts the comments.
Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.