Filters and Functions
Filters transform a value through the pipe, functions produce one, and both take arguments in an order worth memorising.
{{ value|filter(arg1, arg2)|other_filter }} {{ function(arg1) }}A filter transforms a value. You write it after a pipe: {{ title|upper }}. The value on the left becomes the filter's first argument, so that line is strtoupper($title) written the other way round. A function produces a value from its arguments alone and is written as a call: {{ path('app_article_index') }}. That is the whole difference. A filter needs something on its left; a function does not.
Chaining
Pipes apply left to right. {{ body|striptags|trim|slice(0, 120) }} removes tags, then trims, then takes the first 120 characters. The order is the order you read.
The daily set
For strings: upper, lower, title, trim, striptags, nl2br, slice, length. For collections: length, first, last, join, sort, keys, merge, filter, map. For anything that may be missing: default.
Two argument orders to remember
|date(format, timezone) takes a PHP date format string, with F j, Y H:i as the default. {{ published_at|date('d/m/Y', 'Europe/Paris') }} converts the timezone as it formats; passing false keeps the object's own timezone. |number_format(decimals, decimal_point, thousand_sep) starts with the number of decimals, not the number itself, because the number is already on the left of the pipe.
Functions you will reach for in a Stage 1 template: path() and url() for routes, asset() for a file in the public directory, include() for a fragment, dump() while debugging.
Example
// src/Money/MoneyFormatter.php
declare(strict_types=1);
namespace App\Money;
final class MoneyFormatter
{
public function __construct(
private readonly string $symbol = '$',
) {
}
public function format(int $cents): string
{
// PHP: number_format($num, $decimals, $decimal_separator, $thousands)
// Twig: the same arguments, with the number moved to the left
// of the pipe: {{ (cents / 100)|number_format(2, '.', ',') }}
return $this->symbol . number_format($cents / 100, 2, '.', ',');
}
}
// $formatter = new MoneyFormatter();
// echo $formatter->format(1234567);
// Output: $12,345.67When to use it
- Trim and truncate article bodies into a summary on a list page.
- Format a timestamp once for a datetime attribute and once for the reader.
- Print a price stored as integer cents with two decimals and thousands separators.
More examples
Chaining filters for a list page
Each pipe hands its result to the next filter, so the line reads in execution order. join(', ') is the Twig spelling of implode(', ', $tags), and default() covers the empty case without an extra condition. This is the shape of almost every index page you will write.
{# templates/article/index.html.twig #}
{% for article in articles %}
<h2>{{ article.title|title }}</h2>
{# Tags out, whitespace off, first 120 characters, then an ellipsis. #}
<p>{{ article.body|striptags|trim|slice(0, 120) }}...</p>
<p>{{ article.tags|sort|join(', ')|default('untagged') }}</p>
{% endfor %}
<p>{{ articles|length }} articles</p>Dates and numbers
The |date filter accepts anything PHP can turn into a date, including the string 'now'. Pass 'c' when the value is going into a datetime attribute and a human format for the visible text. Use this pair of lines whenever a template shows a timestamp to a reader and to a machine at the same time.
{# templates/article/show.html.twig #}
{# Default format is 'F j, Y H:i'. #}
<p>{{ article.publishedAt|date }}</p>
{# A PHP format string, and an explicit timezone to convert into. #}
<time datetime="{{ article.publishedAt|date('c') }}">
{{ article.publishedAt|date('j M Y', 'Europe/Paris') }}
</time>
{# false keeps the timezone already on the object. #}
<p>{{ article.publishedAt|date('H:i', false) }}</p>
{# decimals, decimal point, thousands separator. #}
<p>{{ (article.priceCents / 100)|number_format(2, '.', ',') }}</p>
{# The parentheses matter: without them the filter applies to 100. #}The PHP each filter stands for
Seeing the function call next to the filter removes the mystery: a filter is a function whose first argument sits on the left of the pipe. Read this when you cannot remember a filter's argument order, because the PHP function it wraps usually has the same one minus the value.
// A scratch file to show the equivalences; a template does this with pipes.
declare(strict_types=1);
$title = ' symfony templates ';
$tags = ['routing', 'http', 'twig'];
$when = new DateTimeImmutable('2026-03-01 09:30:00');
echo trim($title); // {{ title|trim }}
// Output: symfony templates
echo implode(', ', $tags); // {{ tags|join(', ') }}
// Output: routing, http, twig
echo count($tags); // {{ tags|length }}
// Output: 3
echo $when->format('j M Y H:i'); // {{ when|date('j M Y H:i') }}
// Output: 1 Mar 2026 09:30
Discussion