The View

The View

After reading the first part of this tutorial, you have decided that Symfony was worth another 10 minutes. In this second part, you will learn more about Twig, the fast, flexible, and secure template engine for PHP. Twig makes your templates more readable and concise; it also makes them more friendly for web designers.

Getting familiar with Twig

The official Twig documentation is the best resource to learn everything about this new template engine. This section just gives you a quick overview of its main concepts.

A Twig template is a text file that can generate any type of content (HTML, CSS, JavaScript, XML, CSV, LaTeX, ...). Twig elements are separated from the rest of the template contents using any of these delimiters:

  • {{ ... }}: prints the content of a variable or the result of an expression;
  • {% ... %}: controls the logic of the template; it is used for example to execute for loops and if statements;
  • {# ... #}: allows including comments inside templates.

Below is a minimal template that illustrates a few basics, using two variables page_title and navigation, which would be passed into the template:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
    <head>
        <title>{{ page_title }}</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>

        <ul id="navigation">
            {% for item in navigation %}
                <li><a href="{{ item.url }}">{{ item.label }}</a></li>
            {% endfor %}
        </ul>
    </body>
</html>

To render a template in Symfony, use the render method from within a controller and pass the variables needed as an array using the optional second argument:

1
2
3
$this->render('AcmeDemoBundle:Demo:hello.html.twig', array(
    'name' => $name,
));

Variables passed to a template can be strings, arrays, or even objects. Twig abstracts the difference between them and lets you access "attributes" of a variable with the dot (.) notation. The following code listing shows how to display the content of a variable depending on the type of the variable passed by the controller:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{# 1. Simple variables #}
{# array('name' => 'Fabien') #}
{{ name }}

{# 2. Arrays #}
{# array('user' => array('name' => 'Fabien')) #}
{{ user.name }}

{# alternative syntax for arrays #}
{{ user['name'] }}

{# 3. Objects #}
{# array('user' => new User('Fabien')) #}
{{ user.name }}
{{ user.getName }}

{# alternative syntax for objects #}
{{ user.name() }}
{{ user.getName() }}

Decorating Templates

More often than not, templates in a project share common elements, like the well-known header and footer. Twig solves this problem elegantly with a concept called "template inheritance". This feature allows you to build a base "layout" template that contains all the common elements of your site and defines "blocks" that child templates can override.

The hello.html.twig template uses the extends tag to indicate that it inherits from the common layout.html.twig template:

1
2
3
4
5
6
7
8
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}

{% block title "Hello " ~ name %}

{% block content %}
    <h1>Hello {{ name }}!</h1>
{% endblock %}

The AcmeDemoBundle::layout.html.twig notation sounds familiar, doesn't it? It is the same notation used to reference a regular template. The :: part simply means that the controller element is empty, so the corresponding file is directly stored under the Resources/views/ directory of the bundle.

Now, simplify the layout.html.twig template:

1
2
3
4
5
{# src/Acme/DemoBundle/Resources/views/layout.html.twig #}
<div>
    {% block content %}
    {% endblock %}
</div>

The {% block %} tags tell the template engine that a child template may override those portions of the template. In this example, the hello.html.twig template overrides the content block, meaning that the "Hello Fabien" text is rendered inside the <div> element.

Using Tags, Filters, and Functions

One of the best feature of Twig is its extensibility via tags, filters, and functions. Take a look at the following sample template that uses filters extensively to modify the information before displaying it to the user:

1
2
3
4
5
6
7
<h1>{{ article.title|trim|capitalize }}</h1>

<p>{{ article.content|striptags|slice(0, 1024) }}</p>

<p>Tags: {{ article.tags|sort|join(", ") }}</p>

<p>Next article will be published on {{ 'next Monday'|date('M j, Y')}}</p>

Don't forget to check out the official Twig documentation to learn everything about filters, functions and tags.

Including other Templates

The best way to share a snippet of code between several templates is to create a new template fragment that can then be included from other templates.

First, create an embedded.html.twig template:

1
2
{# src/Acme/DemoBundle/Resources/views/Demo/embedded.html.twig #}
Hello {{ name }}

And change the index.html.twig template to include it:

1
2
3
4
5
6
7
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}

{# override the body block from embedded.html.twig #}
{% block content %}
    {{ include("AcmeDemoBundle:Demo:embedded.html.twig") }}
{% endblock %}

Embedding other Controllers

And what if you want to embed the result of another controller in a template? That's very useful when working with Ajax, or when the embedded template needs some variable not available in the main template.

Suppose you've created a topArticlesAction controller method to display the most popular articles of your website. If you want to "render" the result of that method (e.g. HTML) inside the index template, use the render function:

1
2
{# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #}
{{ render(controller("AcmeDemoBundle:Demo:topArticles", {'num': 10})) }}

Here, the AcmeDemoBundle:Demo:topArticles string refers to the topArticlesAction action of the Demo controller, and the num argument is made available to the controller:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// src/Acme/DemoBundle/Controller/DemoController.php

class DemoController extends Controller
{
    public function topArticlesAction($num)
    {
        // look for the $num most popular articles in the database
        $articles = ...;

        return $this->render('AcmeDemoBundle:Demo:topArticles.html.twig', array(
            'articles' => $articles,
        ));
    }

    // ...
}

Including Assets: Images, JavaScripts and Stylesheets

What would the Internet be without images, JavaScripts, and stylesheets? Symfony provides the asset function to deal with them easily:

1
2
3
<link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" />

<img src="{{ asset('images/logo.png') }}" />

The asset function's main purpose is to make your application more portable. Thanks to this function, you can move the application root directory anywhere under your web root directory without changing anything in your template's code.

Final Thoughts

Twig is simple yet powerful. Thanks to layouts, blocks, templates and action inclusions, it is very easy to organize your templates in a logical and extensible way. However, if you're not comfortable with Twig, you can always use PHP templates inside Symfony without any issues.

You have only been working with Symfony for about 20 minutes, but you can already do pretty amazing stuff with it. That's the power of Symfony. Learning the basics is easy, and you will soon learn that this simplicity is hidden under a very flexible architecture.

But I'm getting ahead of myself. First, you need to learn more about the controller and that's exactly the topic of the next part of this tutorial. Ready for another 10 minutes with Symfony?

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License .