Simpler Macros in Twig Templates
June 21, 2019 • Published by Javier Eguiluz
Macros are one of the most important features of the Twig template language to avoid repetitive contents. In Twig 2.11, usage of macros was simplified and other features were added to help you work with macros.
Automatic macro import
Contributed by
Fabien Potencier
in #3012.
Macros are similar to PHP functions because you can pass arguments to them and the contents generated inside the macro are returned to include them in the place where the macro is called.
On symfony.com we use macros for example to display the "contributor box" in several places to highlight our amazing Symfony code and docs contributors:
1 2 3 4 5 6
{% macro contributor_details(contributor, vertical_layout = false) %}
<div class="d-flex">
<img class="avatar {{ vertical_layout ? 'm-b-15' }}" src="...">
Thanks {{ contributor.name }} for being a Symfony contributor.
</div>
{% endmacro %}
Before calling to a macro in a template you must import it, even if the macro
is defined in the same template. This behavior always felt confusing to some
people and made using macros a bit annoying. Starting from Twig 2.11, we've fixed
that and macros defined in the same template are imported automatically
under the special variable _self
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{# templates/some-template.html.twig #}
{% macro contributor_details(contributor, vertical_layout = false) %}
<div class="d-flex">
<img class="avatar {{ vertical_layout ? 'm-b-15' }}" src="...">
Thanks {{ contributor.name }} for being a Symfony contributor.
</div>
{% endmacro %}
{# ... #}
{% for contributor in contributors %}
{# you don't have to import the macro before using it #}
{{ _self.contributor_details(contributor) }}
{% endfor %}
This automatic import also works for macros themselves, so you can call to a macro inside another macro of the same template without importing it explicitly (this also works for any macro imported globally in the template):
1 2 3 4 5 6 7 8 9
{% macro contributor_details(contributor, vertical_layout = false) %}
{# ... #}
{% endmacro %}
{% macro contributor_list(contributors) %}
{% for contributor in contributors %}
{{ _self.contributor_details(contributor) }}
{% endfor %}
{% endmacro %}
Checking for macro existence
Contributed by
Fabien Potencier
in #3014.
Another new feature introduced in Twig 2.11 is the support for checking the
existence of macros before calling to them thanks to the is defined
test.
It works both for macros imported explicitly and for auto-imported macros:
1 2 3 4 5
{% import 'templates/_macros.html.twig' as macros %}
{% if macros.contributor_details is defined %}
{# ... #}
{% endif %}
Macros Scoping
Contributed by
Fabien Potencier
in #3009.
The scoping rules that define which macros are available inside each element of each template have been clearly defined as of Twig 2.11 as follows:
- Imported macros are available in all blocks and other macros defined in the current template, but they are not available in included templates or child templates (you need to explicitly re-import macros in each template).
- Macros imported inside a
{% block %}
tag are only defined inside that block and override other macros with the same name imported in the template. Same for macros imported explicitly inside a{% macro %}
tag.
Better macro errors
Recent Twig versions have fixed some edge-cases related to macros (e.g. calling a macro imported in a block from a nested block, etc.) and improved other error messages to make them crystal clear and help you debug issues more easily.
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Thank you - yet so small, but soooo much better
Will
{% if _self.contributor_details is defined %} {# ... #} {% endif %}
also work?
I don't know if it's already been mentioned anywhere, but it would be very handy if such "first-class" Citizens like Twig and/or Doctrine were marked with exact version in the debug toolbar (?panel=config).
It is great to see that my Symfony is on version 4.3.1 but I would also like to know that my Twig is on 2.11 and my Doctrine ORM is on 2.6.3
@Martin yes, that test will work too.
Awesome! I've been working with Symfony for almost five years and did not know there was a macro feature. Shame on me. Thanks for this great information!
thanks
Thanks for fixing this annoying 'bug'
Wasn't it better to make some kind of autoload function where twig search for the macro file inside the template folder or an yaml setting where you specify the default macro folder ?
@Danny van der Knaap
--> Shameless plug warning! ;) <-- You could check out https://github.com/redantnl/twig-components-bundle for an approach that is similar to that, and adds more.
@Danny van der Knaap You could look at https://github.com/redantnl/twig-components-bundle for an approach to that :)