diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index a323461885d..6bc8b99ce7a 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -158,30 +158,23 @@ Twig Extension & Autoconfiguration Thanks to Symfony's service handling, you can *extend* Symfony in many ways, like by creating an event subscriber or a security voter for complex authorization -rules. Let's add a new filter to Twig called ``greet``. How? Create a class -that extends ``AbstractExtension``:: +rules. Let's add a new filter to Twig called ``greet``. How? Create a simple class +with your logic:: // src/Twig/GreetExtension.php namespace App\Twig; use App\GreetingGenerator; - use Twig\Extension\AbstractExtension; - use Twig\TwigFilter; + use Twig\Attribute\AsTwigFilter; - class GreetExtension extends AbstractExtension + class GreetExtension { public function __construct( private GreetingGenerator $greetingGenerator, ) { } - public function getFilters(): array - { - return [ - new TwigFilter('greet', [$this, 'greetUser']), - ]; - } - + #[AsTwigFilter('greet')] public function greetUser(string $name): string { $greeting = $this->greetingGenerator->getRandomGreeting(); @@ -197,8 +190,8 @@ After creating just *one* file, you can use this immediately: {# templates/default/index.html.twig #} {# Will print something like "Hey Symfony!" #}

{{ name|greet }}

- -How does this work? Symfony notices that your class extends ``AbstractExtension`` + +How does this work? Symfony notices that your class uses a Twig attribute and so *automatically* registers it as a Twig extension. This is called autoconfiguration, and it works for *many* many things. Create a class and then extend a base class (or implement an interface). Symfony takes care of the rest. diff --git a/reference/attributes.rst b/reference/attributes.rst index a8399dafe28..7878446351c 100644 --- a/reference/attributes.rst +++ b/reference/attributes.rst @@ -123,6 +123,8 @@ Twig ~~~~ * :ref:`Template ` +* :ref:`AsTwigFilter ` +* :ref:`AsTwigFunction ` Symfony UX ~~~~~~~~~~ diff --git a/templates.rst b/templates.rst index c6312abb33c..3ca54276540 100644 --- a/templates.rst +++ b/templates.rst @@ -1553,23 +1553,20 @@ as currency: {# pass in the 3 optional arguments #} {{ product.price|price(2, ',', '.') }} -Create a class that extends ``AbstractExtension`` and fill in the logic:: +.. _templates-twig-filter-attribute: + +Create a class with a method that contains the filter logic, then add +the ``#[AsTwigFilter]`` attribute to define the name and options of +the Twig filter:: // src/Twig/AppExtension.php namespace App\Twig; - use Twig\Extension\AbstractExtension; - use Twig\TwigFilter; + use Twig\Attribute\AsTwigFilter; - class AppExtension extends AbstractExtension + class AppExtension { - public function getFilters(): array - { - return [ - new TwigFilter('price', [$this, 'formatPrice']), - ]; - } - + #[AsTwigFilter('price')] public function formatPrice(float $number, int $decimals = 0, string $decPoint = '.', string $thousandsSep = ','): string { $price = number_format($number, $decimals, $decPoint, $thousandsSep); @@ -1579,24 +1576,19 @@ Create a class that extends ``AbstractExtension`` and fill in the logic:: } } -If you want to create a function instead of a filter, define the -``getFunctions()`` method:: +.. _templates-twig-function-attribute: + +If you want to create a function instead of a filter, use the +``#[AsTwigFunction]`` attribute:: // src/Twig/AppExtension.php namespace App\Twig; - use Twig\Extension\AbstractExtension; - use Twig\TwigFunction; + use Twig\Attribute\AsTwigFunction; - class AppExtension extends AbstractExtension + class AppExtension { - public function getFunctions(): array - { - return [ - new TwigFunction('area', [$this, 'calculateArea']), - ]; - } - + #[AsTwigFunction('area')] public function calculateArea(int $width, int $length): int { return $width * $length; @@ -1608,6 +1600,16 @@ If you want to create a function instead of a filter, define the Along with custom filters and functions, you can also register `global variables`_. +.. versionadded:: 7.3 + + Support for the ``#[AsTwigFilter]`` and ``#[AsTwigFunction]`` attributes was introduced in Symfony 7.3. + Previously, you had to extend the ``AbstractExtension`` class, and override the + ``getFilters()`` and ``getFunctions()`` methods. + +When using autoconfiguration, the tag ``twig.attribute_extension`` is added automatically +when a Twig attribute is used on a method of a class. Otherwise, when autoconfiguration is not enabled, +it needs to be added in the service definition. + Register an Extension as a Service .................................. @@ -1631,9 +1633,10 @@ this command to confirm that your new filter was successfully registered: Creating Lazy-Loaded Twig Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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 +When using attributes to extend Twig, the services are initialized only when +the functions or filters are used to render the template. But in case you use the +classic approach by extending the ``AbstractExtension`` class, Twig initialize all extensions before +rendering rendering any template, even if the template doesn't use an extension. If extensions don't define dependencies (i.e. if you don't inject services in