При работе с Ajax бывает надо, чтобы одни и те же данные отображались с помощью разных шаблонов. Но шаблоны Twig, это скомпилированные PHP классы, т.е. обернуть тэг extends тэгом if не получиться:
{# это не работает #}
{% if request.ajax %}
{% extends "base.html" %}
{% endif %}
{% block content %}
Некий контент...
{% endblock %}
Один из способов решения этой проблемы - создание двух шаблонов:
{# index.html #}
{% extends "layout.html" %}
{% block content %}
{% include "index_for_ajax.html" %}
{% endblock %}
{# index_for_ajax.html #}
Некий контент...
Теперь перенесем выбор шаблона для отображения в контроллер:
$twig->render($request->isAjax() ? 'index_for_ajax.html' : 'index.html');
Когда подключается шаблон, его название не обязательно может быть строкой. Например, это может быть значение переменной:
{% include var ~ '_foo.html' %}
Если переменная var содержит значение "index", будет подключен шаблон index_foo.html.
На самом деле, названием шаблона может быть любое выражение, к примеру:
{% include var|default('index') ~ '_foo.html' %}
Twig позволяет немного настроить синтаксис шаблонов. Эту возможность лучше не использовать, но если очень хочется, то можно :)
Для того, чтобы изменить разделители блоков, вам надо создать собственный объект Twig_Lexer:
$twig = new Twig_Environment(); $lexer = new Twig_Lexer($twig, array( 'tag_comment' => array('{#', '#}'), 'tag_block' => array('{%', '%}'), 'tag_variable' => array('{{', '}}'), )); $twig->setLexer($lexer);
Дальше приведем несколько примеров, имитирующих синтаксис известных шаблонизаторов:
// Синтаксис Ruby erb
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('<%#', '%>'),
'tag_block' => array('<%', '%>'),
'tag_variable' => array('<%=', '%>'),
));
// Синтаксис SGML комментариев
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('<!--#', '-->'),
'tag_block' => array('<!--', '-->'),
'tag_variable' => array('${', '}'),
));
// Как Smarty
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('{*', '*}'),
'tag_block' => array('{', '}'),
'tag_variable' => array('{$', '}'),
));
Когда Twig получает переменную типа article.title, он пытается найти эту публичное свойство title у объекта article.
Если такого свойства нет, но оно определяется динамически с помощью метода __get(), то вы можете его использовать, если определите так же метод __isset(), как в примере ниже:
class Article { public function __get($name) { if ('title' == $name) { return ''Заголовок''; } // бросаем исключение } public function __isset($name) { if ('title' == $name) { return true; } return false; } }
Иногда бывает надо сделать шаблон, который хранит информацию о переменных, передаваемых ему из приложения. Но, по-умолчанию, скомпилированный шаблон может только принимать массив параметров.
Когда шаблон рендериться, вы можете передать ему переменные, но это не очень хорошо. Есть решение лучше.
По умолчанию все скомпилированные шаблоны расширяют базовый класс Twig_Template. Базовый класс можно определить с помощью параметра base_template_class:
$twig = new Twig_Environment($loader, array('base_template_class' => 'ProjectTemplate'));
Теперь все шаблоны будут наследоваться от нашего класса ProjectTemplate. Создадим его и добавим getter/setter методы для взаимодействия между шаблонами и вашим приложением:
class ProjectTemplate extends Twig_Template { protected $context = null; public function setContext($context) { $this->context = $context; } public function getContext() { return $this->context; } }
Теперь, с помощью setter, вы можете установить контекст шаблона и использовать getter для получения его.
Иногда, при использовании вложенных циклов, есть необходимость получить данные внешнего цикла. Они доступны через переменную loop.parent. Например, у нас есть следующие данные:
$data = array(
'topics' => array(
'topic1' => array('Сообщение 1 в теме 1', 'Сообщение 2 в теме 1'),
'topic2' => array('Сообщение 1 в теме 2', 'Сообщение 2 в теме 2'),
),
);
А этот шаблон отобразит все сообщения во всех темах:
{% for topic, messages in topics %}
* {{ loop.index }}: {{ topic }}
{% for message in messages %}
- {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
{% endfor %}
{% endfor %}
Получиться что-то вроде этого:
* 1: topic1
- 1.1: Сообщение 1 в теме 1
- 1.2: Сообщение 2 в теме 1
* 2: topic2
- 2.1: Сообщение 1 в теме 2
- 2.2: Сообщение 2 в теме 2
Во внутреннем цикле используется переменная loop.parent для доступа к текущей итерации внешнего цикла. Индекс topic определяется с помощью переменной loop.parent.loop.index.
(Добавленно в Twig 0.9.6)
По умолчанию результат выполнения макроса выводиться в браузер. Если вы хотите передать результат выполнения одного макроса в другой - используйте тэг set:
{% import "form_elements.html" as form %}
{% set theinput %}
{{ form.input('test', 'text', 'Value') }}
{% endset %}
{{ form.row('Label', theinput) }}
Если вы используете расширение Twig I18n, скорее всего вам хочется легко извлечь строки для перевода из шаблонов. Увы, утилита xgettext не понимает шаблоны Twig. Но есть простое решение этой проблемы: так как Twig конвертирует шаблоны в PHP файлы, вы можете применить xgettext к кэшу шаблонов.
Напишем скрипт, который генерирует кэш для всех ваших шаблонов. Например, такой:
$tplDir = dirname(__FILE__).'/templates'; $tmpDir = '/tmp/cache/'; $loader = new Twig_Loader_Filesystem($tplDir); // auto-reload - принудительное обновление кэша $twig = new Twig_Environment($loader, array( 'cache' => $tmpDir, 'auto_reload' => true )); $twig->addExtension(new Twig_Extension_I18n()); // сконфигурируйте Twig, как вам надо // переберем все шаблоны foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tplDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { // принудительно скомпилируем $twig->loadTemplate(str_replace($tplDir.'/', '', $file)); }
Теперь можно использовать утилиту xgettext как с обычным PHP кодом:
xgettext --default-domain=messages -p ./locale --from-code=UTF-8 -n --omit-header -L PHP /tmp/cache/*.php