If you are already familiar with writing Twig templates, you might want to skip this part and directly head over to the next chapter. For everyone else, here comes a quick Twig 101.
Twig is the standard way to write templates in Symfony. And that is for a reason: It features a wide range of powerful methods to structure and reuse templates, has an easy-to-use syntax to access objects, helpers to transform data, built-in whitespace control, string interpolation features, macros, and — really — a ton more…
But ultimately, the biggest selling point for us is security. Twig embraces output encoding, something that we desperately try to move to for a long time. That is, why Twig has been made a core requirement and will replace our old existing template system and also why you should familiarize yourself with it.
Twig templates have their own syntax, but don’t be afraid, you’ll quickly find your way. Here, is an example of a PHP template, that was translated into an equivalent Twig template. (In case you’re new to Contao, you might want to directly focus on the Twig side and ignore the old stuff. 😉)
<?php */ Old stuff */ ?>
<div class="about-me">
<h2><?= $this->name ?></h2>
<p>I am <?= round($this->age) ?> years old.</p>
<ul class="hobby-list">
<?php foreach $this->hobbies as $hobby: ?>
<li><?= ucfirst($hobby) ?></li>
<?php endforeach; ?>
</ul>
</div>
{# New stuff! #}
<div class="about-me">
<h2>{{ name }}</h2>
<p>I am {{ age|round }} years old.</p>
<ul class="hobby-list">
{% for hobby in hobbies %}
<li>{{ hobby|capitalize }}</li>
{% endfor %}
</ul>
</div>
{{ foo }}
,for
to loop over an array — we wrap them in {%
and %}
,|foo
and functions bar()
.{#
and #}
.Twig is very well documented. A good place to start is the Twig for template designers section that covers syntax details as well as helpful IDE plugins for autocompletion and syntax highlighting.
For quickly trying something out, you can use Twig fiddle, an online playground. Take a look at this demo fiddle for instance.
Extending Twig yourself is easy but there are also already a lot of Twig extensions in the wild, including some official ones, called the twig-extra bundles. In Contao, the latter can simply be installed with Composer or the Contao Manager and are directly ready to be used (no need to configure or register anything).
composer require twig/intl-extra
{{ '1000000'|format_currency('EUR') }}
{# €1,000,000.00 #}
We also encourage you to extend Twig yourself. There are a lot of extension points with filters and functions being the easiest to start with. Have a look in the official docs, where things are explained in more detail.
If you created PHP templates in the past, and are missing functionality that you previously would have implemented directly in the templates, using filters and functions is the way to go. As a nice side effect, you can now make use of your custom implementation in any template.
Let’s implement a simple |rot13
filter in our application, that will scramble strings by shifting every letter by 13
places in the alphabet (Abcd
→ Nopq
). That’s apparently so useful, that PHP already has a built-in function for
this: str_rot13()
. Nice.
Go ahead and create a class, that is extending Twig\Extension\AbstractExtension
in the src/Twig
folder of your
application. You can have as many extensions as you want but in an application, you would typically only use a single
AppExtension
. Then, override the getFilters()
function and register a new filter:
// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
// Register a new filter named "rot13" and tell Twig which method to execute
new TwigFilter('rot13', [$this, 'rotateString']),
];
}
// The first argument ($value) is the string, the filter is applied on
public function rotateString(string $value): string
{
return str_rot13($value);
}
}
And… that’s all there is to it. Our filter is now ready and can be used in any template:
{% set secret_cms = 'Pbagnb' %}
Turns out "{{ secret_cms }}" means "{{ secret_cms|rot13 }}".
{# Turns out "Pbagnb" means "Contao". #}