Making data from your app's services available in twig templates can be done by either:
- Injecting the service/data into the template when rendering.
- Creating a twig extension that has access to the service/data.
For #1, this isn't always a viable option (ie you need this data in your layout). With #2, there is a bit of boilerplate and if done incorrectly (ie not using a runtime or service proxy for heavy services), it could lead to performance issues.
This bundle provides an easy way to make functions, static methods, service methods, and even full service objects available in your twig templates.
composer require zenstruck/twig-service-bundle
Note
If not added automatically by symfony/flex
, enable ZenstruckTwigServiceBundle
.
Note
The output for the following functions/filters will be escaped. If your
function\filter returns html that you don't want escaped, use the |raw
filter.
You can mark any public method in your configured services with the #[AsTwigFunction]
attribute to make them available within your twig templates with the fn()
twig
function/filter:
// ...
use Zenstruck\Twig\AsTwigFunction;
class SomeService
{
// ...
#[AsTwigFunction] // will be available as "fn_someMethod()" in twig
public function someMethod($arg1, $arg2): string
{
// ...
}
#[AsTwigFunction('alias')] // will be available as "fn_alias()" in twig
public function anotherMethod($arg1, $arg2): string
{
// ...
}
}
In your twig template, use the fn()
function/filter to call:
{# as a function: #}
{{ fn('someMethod', 'foo', 'bar') }}
{{ fn('alias', 'foo', 'bar') }}
{# as a filter: #}
{{ 'foo'|fn('someMethod', 'bar') }}
{{ 'foo'|fn('alias', 'bar') }}
Dynamic functions/filters are made available. The following is equivalent to above:
{# as a function: #}
{{ fn_someMethod('foo', 'bar') }}
{{ fn_alias('foo', 'bar') }}
{# as a filter: #}
{{ 'foo'|fn_someMethod('bar') }}
{{ 'foo'|fn_alias('bar') }}
You can mark any of your custom functions with the #[AsTwigFunction]
attribute
to make them available within your twig templates with the fn()
twig function\filter:
use Zenstruck\Twig\AsTwigFunction;
#[AsTwigFunction] // will be available as "some_function" in twig
function some_function($arg1, $arg2): string
{
// ...
}
#[AsTwigFunction('alias')] // will be available as "alias" in twig
function another_function($arg1, $arg2): string
{
// ...
}
In your twig template, use the fn()
function/filter to call:
{# as a function: #}
{{ fn('some_function', 'foo', 'bar') }}
{{ fn('alias', 'foo', 'bar') }}
{# as a filter: #}
{{ 'foo'|fn('some_function', 'bar') }}
{{ 'foo'|fn('alias', 'bar') }}
Dynamic functions/filters are made available. The following is equivalent to above:
{# as a function: #}
{{ fn_some_function('foo', 'bar') }}
{{ fn_alias('foo', 'bar') }}
{# as a filter: #}
{{ 'foo'|fn_some_function('bar') }}
{{ 'foo'|fn_alias('bar') }}
If you need to make functions, static/service methods available in your twig templates for code you do not control (ie internal PHP functions/3rd party package), you can configure these in the bundle config:
zenstruck_twig_service:
functions:
- strlen # available as "fn_strlen()" in twig
- [service.id, serviceMethod] # available as "fn_serviceMethod()" in twig
- [Some\Class, somePublicStaticMethod] # available as "fn_somePublicStaticMethod()" in twig
# use the array key to customize the name
functions:
len: strlen # available as "fn_len()" in twig
custom: [service.id, serviceMethod] # available as "fn_custom()" in twig
alias: [Some\Class, somePublicStaticMethod] # available as "fn_alias()" in twig
Mark any service you'd like to make available in twig templates with the #[AsTwigService]
.
Note
While you can mark any service as a twig service, it is not recommended to mark services that have nothing to do with templating (ie repositories) as such. You can think of twig services as lightweight-lazy-twig-extension-functions whose purpose is to break up/simplify large custom twig extensions.
namespace App\Twig\Service;
// ...
use Zenstruck\Twig\AsTwigService;
#[AsTwigService(alias: 'posts')]
class PostService
{
public function __construct(private PostRepository $repo)
{
}
/**
* @return Post[]
*/
public function latestPosts(int $number = 10): array
{
return $this->repo->findLatestPosts($number);
}
}
You're now ready to access the service in any twig template:
{% for post in service('posts').latestPosts(5) %}
{# ... #}
{% endfor %}
Each service alias is made available as a dynamic function. The following is equivalent to above:
{% for post in service_posts().latestPosts(5) %}
{# ... #}
{% endfor %}
You can turn any twig service into a twig filter by having it implement __invoke()
:
namespace App\Twig\Service;
// ...
use Zenstruck\Twig\AsTwigService;
#[AsTwigService(alias: 'image_transformer')]
class ImageTransformer
{
public function __invoke(string $imageUrl, string ...$transformations): string
{
// adds transformation to url and returns new url
}
}
In your template, use the service
twig filter:
{{ url|service('image_transformer', 'square-200', 'watermark') }}
Each service alias is made available as a dynamic filter. The following is equivalent to above:
{{ url|service_image_transformer('square-200', 'watermark') }}
You can access any service container parameter with the provided parameter()
twig function:
{% for locale in parameter('kernel.enabled_locales') %}
{# ... #}
{% endfor %}
Use this command to list all functions/filters/services configured by this bundle and available in your twig templates.
Note
This command is only available when debug: true
.
bin/console zenstruck:twig-service:list
Available Functions/Filters
---------------------------
// As function: call with fn('{alias}', {...args}) or fn_{alias}({...args})
// As filter: use as {value}|fn('{alias}', {...args}) or {value}|fn_{alias}({...args})
---------- ---------------------------------------------
Alias Callable
---------- ---------------------------------------------
strlen strlen
generate @router->generate()
---------- ---------------------------------------------
Available Services
------------------
// Access via service('{alias}') or service_{alias}()
// If invokable, use as {value}|service('{alias}', {...args}) or {value}|service_{alias}({...args})
------- -------------------- ------------
Alias Service Invokable?
------- -------------------- ------------
foo App\SomeService yes
bar App\AnotherService no
------- -------------------- ------------
zenstruck_twig_service:
# Callables to make available with fn() twig function/filter
functions:
# Examples:
0: strlen # available as "strlen"
alias: [Some\Class, somePublicStaticMethod] # available as "alias"