How to Write a custom Twig Extension

Twig Extensions allow the creation of custom functions, filters, and more to use in your Twig templates. Before writing your own Twig extension, check if the filter/function that you need is already implemented in:

Twig 拡張機能を使用すると、Twig テンプレートで使用するカスタム関数、フィルターなどを作成できます。独自の Twig 拡張機能を作成する前に、必要なフィルター/関数が既に実装されているかどうかを確認してください。

Create the Extension Class

Suppose you want to create a new filter called price that formats a number as currency:

数値を通貨としてフォーマットする price という名前の新しいフィルターを作成するとします。
1
2
3
4
{{ product.price|price }}

{# pass in the 3 optional arguments #}
{{ product.price|price(2, ',', '.') }}

Create a class that extends AbstractExtension and fill in the logic:

AbstractExtension を拡張するクラスを作成し、ロジックを入力します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Twig/AppExtension.php
namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class AppExtension extends AbstractExtension
{
    public function getFilters()
    {
        return [
            new TwigFilter('price', [$this, 'formatPrice']),
        ];
    }

    public function formatPrice(float $number, int $decimals = 0, string $decPoint = '.', string $thousandsSep = ','): string
    {
        $price = number_format($number, $decimals, $decPoint, $thousandsSep);
        $price = '$'.$price;

        return $price;
    }
}

If you want to create a function instead of a filter, define the getFunctions() method:

フィルターの代わりに関数を作成する場合は、getFunctions() メソッドを定義します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/Twig/AppExtension.php
namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class AppExtension extends AbstractExtension
{
    public function getFunctions()
    {
        return [
            new TwigFunction('area', [$this, 'calculateArea']),
        ];
    }

    public function calculateArea(int $width, int $length): int
    {
        return $width * $length;
    }
}

Tip

ヒント

Along with custom filters and functions, you can also register global variables.

カスタム フィルターと関数に加えて、グローバル変数を登録することもできます。

Register an Extension as a Service

Next, register your class as a service and tag it with twig.extension. If you're using the default services.yaml configuration, you're done! Symfony will automatically know about your new service and add the tag.

次に、クラスをサービスとして登録し、twig.extension でタグ付けします。デフォルトの services.yaml 構成を使用している場合は、これで完了です。 symfony は自動的に新しいサービスを認識し、タグを追加します。

You can now start using your filter in any Twig template. Optionally, execute this command to confirm that your new filter was successfully registered:

任意の Twig テンプレートでフィルターを使用できるようになりました。必要に応じて、次のコマンドを実行して、新しいフィルターが正常に登録されたことを確認します。
1
2
3
4
5
# display all information about Twig
$ php bin/console debug:twig

# display only the information about a specific filter
$ php bin/console debug:twig --filter=price

Creating Lazy-Loaded Twig Extensions

1.35

1.35

Support for lazy-loaded extensions was introduced in Twig 1.35.0 and 2.4.4.

遅延読み込み拡張機能のサポートは、Twig 1.35.0 および 2.4.4 で導入されました。

Including the code of the custom filters/functions in the Twig extension class is the simplest way to create extensions. However, Twig must initialize all extensions before rendering any template, even if the template doesn't use an extension.

Twig 拡張機能クラスにカスタム フィルター/関数のコードを含めることは、拡張機能を作成する最も簡単な方法です。ただし、テンプレートが拡張機能を使用していない場合でも、Twig はテンプレートをレンダリングする前にすべての拡張機能を初期化する必要があります。

If extensions don't define dependencies (i.e. if you don't inject services in them) performance is not affected. However, if extensions define lots of complex dependencies (e.g. those making database connections), the performance loss can be significant.

拡張機能が依存関係を定義していない場合 (つまり、拡張機能にサービスを注入していない場合)、パフォーマンスは影響を受けません。ただし、拡張機能が多くの複雑な依存関係 (データベース接続を作成する依存関係など) を定義している場合、パフォーマンスの損失が大きくなる可能性があります。

That's why Twig allows decoupling the extension definition from its implementation. Following the same example as before, the first change would be to remove the formatPrice() method from the extension and update the PHP callable defined in getFilters():

そのため、Twig では拡張機能の定義をその実装から切り離すことができます。前と同じ例に従うと、最初の変更は拡張機能から formatPrice() メソッドを削除し、getFilters() で定義された PHPcallable を更新することです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Twig/AppExtension.php
namespace App\Twig;

use App\Twig\AppRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class AppExtension extends AbstractExtension
{
    public function getFilters()
    {
        return [
            // the logic of this filter is now implemented in a different class
            new TwigFilter('price', [AppRuntime::class, 'formatPrice']),
        ];
    }
}

Then, create the new AppRuntime class (it's not required but these classes are suffixed with Runtime by convention) and include the logic of the previous formatPrice() method:

次に、新しい AppRuntime クラスを作成し (必須ではありませんが、これらのクラスには慣例により Runtime というサフィックスが付けられます)、以前の formatPrice() メソッドのロジックを含めます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Twig/AppRuntime.php
namespace App\Twig;

use Twig\Extension\RuntimeExtensionInterface;

class AppRuntime implements RuntimeExtensionInterface
{
    public function __construct()
    {
        // this simple example doesn't define any dependency, but in your own
        // extensions, you'll need to inject services using this constructor
    }

    public function formatPrice(float $number, int $decimals = 0, string $decPoint = '.', string $thousandsSep = ','): string
    {
        $price = number_format($number, $decimals, $decPoint, $thousandsSep);
        $price = '$'.$price;

        return $price;
    }
}

If you're using the default services.yaml configuration, this will already work! Otherwise, create a service for this class and tag your service with twig.runtime.

デフォルトの services.yaml 構成を使用している場合、これは既に機能しています。それ以外の場合は、このクラスのサービスを作成し、サービスに twig.runtime のタグを付けます。