Filters and Functions

Filters transform a value through the pipe, functions produce one, and both take arguments in an order worth memorising.

Syntax{{ 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

Example · php
// 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.67

When 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.

Example · twig
{# 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.

Example · twig
{# 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.

Example · twig
// 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

  • Be the first to comment on this lesson.