Twig: macros, custom filters, globals
When templates multiply, repetition appears: identical form fields, the same price formatting, a sidebar on every page. Twig has tools to describe each such thing once.
Variables in a template
{% set total = price * quantity %}
<p>Total: {{ total }}</p>Tests and operators
{% if user is defined and user.name is not empty %}
{{ user.name }}
{% endif %}
{{ post.subtitle ?? 'No subtitle' }}
{{ 'Hello, ' ~ user.name }}
{{ loop.index is even ? 'even' : 'odd' }}The ~ sign joins strings, ?? supplies a value in place of a missing one.
Macros
A macro is a function that returns markup:
{# templates/_macros.html.twig #}
{% macro badge(text, color = 'gray') %}
<span class="badge badge-{{ color }}">{{ text }}</span>
{% endmacro %}{% import '_macros.html.twig' as ui %}
{{ ui.badge('New', 'green') }}
{{ ui.badge('Draft') }}Your own filter
An extension class adds filters and functions:
// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('price', $this->formatPrice(...)),
];
}
public function formatPrice(float $amount): string
{
return '$' . number_format($amount, 2);
}
}{{ product.price|price }}The class needs no registration: Symfony finds extensions by itself. Functions are added the same way — with the getFunctions() method and the TwigFunction class.
Global variables
Values needed in every template are set in the configuration:
# config/packages/twig.yaml
twig:
globals:
site_name: 'My blog'
support_email: 'help@example.com'<footer>{{ site_name }} · {{ support_email }}</footer>The built-in app variable already holds useful things: app.user, app.request, app.session, app.environment.
Embedding a controller
A "Latest posts" block in the sidebar needs data on every page. Instead of passing it from every controller, the block is rendered by a separate request inside the template:
<aside>
{{ render(controller('App\\Controller\\SidebarController::recent', {limit: 5})) }}
</aside>Debugging
{{ dump(post) }}dump() shows the contents of a variable. Without arguments it shows every variable of the template.
Common mistake
A template is no place for complex calculations or database queries. If long chains of conditions and arithmetic appear in Twig, move them into the controller, an entity method or your own filter. A template should only display ready data.