Последние записи в блоге

Переопределение macro

Автор: Дмитрий Цирульников
Дата:  9 Октября 2014

Наследование в Twig очень мощная штука, но не хватает тут одного - переопределения макросов.
Суть задачи в следующем: есть файл с макросами macro.twig

{% macro test() %}
    Hello {{ _self.test_1() }}
{% endmacro %}

{% macro test_1() %}
    World
{% endmacro %}

Из одного макроса вызываем второй, в результате работы {{ test() }} мы получим уже изрядно намозоливший «Hello World».
У нас же задача переопределить макрос test_1.

Сразу код, комментарии ниже:

{% macro macro_extend( macroName, macroOptions, mergeList ) %}

    {% set source_ %}
        {% for path in mergeList %}
            {{ source(path) }}
        {% endfor %}
    {% endset %}


    {% set args %}
        {% for index in macroOptions|keys %}
            macroOptions[{{ index }}]
        {% endfor %}
    {% endset %}

    {% set controll = '{{ _self.' ~ macroName ~ '(' ~ args ~ ')  }}' %}

    {{ include(template_from_string( source_ ~controll )) }}
{% endmacro %}

Для удобства создаем макрос extend, который как аргументы принимает:

  • macroName - имя вызываемого макроса;
  • macroOptions - передаваемые в макрос аргументы в виде массива, где каждый элемент массива является одним аргументом для вызываемого макроса;
  • mergeList - список файлов, которые будут участвовать в предопределении, чем больше индекс, тем выше приоритет при определении.

Основной смысл затеи - собрать все макросы в одно место и вызвать нужный. Это все можно сделать при помощи spurce, include и template_from_string и магического _self.

Есть одна особенность в парсере твига для макросов: если парсер натыкается на два макроса названные одинаково, то он запомнит последний из них, то есть:

{% macro test() %}
    test 1
{% endmacro %}

{% macro test() %}
    test 2
{% endmacro %}

Результатом вызова {{ test() }} будет test 2. Это-то нам и поможет переопределять макросы так, как нам нужно.
Чтобы сделать вызов определенного макроса из цепочки на лету нам понадобится _self, он ссылается на текущую область видимости, и через нее можно вызвать нужный нам макрос.

Вот, собственно, и вся хитрость, которая облегчит жизнь при построении сложных шаблонов с многочисленными контроллами.

PS: Пример вызова моего макроса

{% from 'macro_extend.twig' import macro_extend %}
{{ macro_extend('test', [], [
    'test.twig',
    'test2.twig'
]) }}
Агрегатор фриланс бирж FreelanceGrab, искать заказы на фрилансе стало еще проще.
8 крупных бирж, удобный поиск и фильтрация по проектам,
моментальное обновление ленты без перезагрузки страницы