Simpler Macros in Twig Templates

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.

Comments

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! ;)
@Danny van der Knaap
You could look at https://github.com/redantnl/twig-components-bundle for an approach to that :)

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.