Volt: Шаблонный движок
Volt — это сверхбыстрый и удобный для дизайнеров шаблонный язык, написанный на C для PHP. Он предоставляет набор помощников для лёгкого написания представлений. Volt тесно интегрирован с другими компонентами Phalcon, а также может использоваться как самостоятельный компонент в ваших приложениях.
Volt вдохновлён Jinja, первоначально созданным Armin Ronacher. Поэтому многие разработчики будут чувствовать себя комфортно, используя тот же синтаксис, что и в подобных шаблонных движках. Синтаксис и возможности Volt были расширены с добавлением новых элементов и, конечно же, с сохранением производительности, к которой разработчики привыкли при работе с Phalcon.
Введение
Представления Volt компилируются в чистый PHP-код, что, по сути, экономит усилия по ручному написанию PHP-кода:
{# app/views/products/show.volt #}
{% block last_products %}
{% for product in products %}
* Name: {{ product.name|e }}
{% if product.status == "Active" %}
Price: {{ product.price + product.taxes/100 }}
{% endif %}
{% endfor %}
{% endblock %}
Активация Volt
Как и другие шаблонные движки, вы можете зарегистрировать Volt в компоненте представления, используя новый расширение или повторно используя стандартное .phtml:
//Registering Volt as template engine
$di->set('view', function() {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
".volt" => 'Phalcon\Mvc\View\Engine\Volt'
));
return $view;
});
Используйте стандартное расширение «.phtml»:
$view->registerEngines(array(
".phtml" => 'Phalcon\Mvc\View\Engine\Volt'
));
Основное использование
Представление состоит из Volt-кода, PHP и HTML. Для переключения в режим Volt доступен набор специальных разделителей. {% ... %} используется для выполнения инструкций, таких как циклы for или присваивание значений, а {{ ... }} выводит результат выражения в шаблон.
Ниже представлен минимальный шаблон, иллюстрирующий несколько основных концепций:
{# app/views/posts/show.phtml #}
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} - An example blog</title>
</head>
<body>
{% if show_navigation %}
<ul id="navigation">
{% for item in menu %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
{% endif %}
<h1>{{ post.title }}</h1>
<div class="content">
{{ post.content }}
</div>
</body>
</html>
Используя Phalcon\Mvc\View, вы можете передавать переменные из контроллера в представления. В приведенном выше примере в представление были переданы три переменные: title, menu и post:
class PostsController extends \Phalcon\Mvc\Controller
{
public function showAction()
{
$post = Post::findFirst();
$this->view->title = $post->title;
$this->view->post = $post;
$this->view->menu = Menu::find();
$this->view->show_navigation = true;
}
}
Переменные
Переменные-объекты могут иметь атрибуты, к которым можно получить доступ, используя синтаксис: foo.bar. Если вы передаёте массивы, вам нужно использовать синтаксис с квадратными скобками: foo[‘bar’]
{{ post.title }} {# for $post->title #}
{{ post['title'] }} {# for $post['title'] #}
Фильтры
Переменные могут быть отформатированы или изменены с помощью фильтров. Оператор «|» используется для применения фильтров к переменным:
{{ post.title|e }}
{{ post.content|striptags }}
{{ name|capitalize|trim }}
Ниже представлен список доступных встроенных фильтров в Volt:
| Фильтр | Описание |
|---|---|
| e | Применяет Phalcon\Escaper->escapeHtml к значению |
| escape | Применяет Phalcon\Escaper->escapeHtml к значению |
| escape_css | Применяет Phalcon\Escaper->escapeCss к значению |
| escape_js | Применяет Phalcon\Escaper->escapeJs к значению |
| escape_attr | Применяет Phalcon\Escaper->escapeHtmlAttr к значению |
| trim | Применяет функцию PHP trim к значению. Удаляет лишние пробелы |
| left_trim | Применяет функцию PHP ltrim к значению. Удаляет лишние пробелы слева |
| right_trim | Применяет функцию PHP rtrim к значению. Удаляет лишние пробелы справа |
| striptags | Применяет функцию PHP striptags к значению. Удаляет HTML-теги |
| slashes | Применяет функцию PHP slashes к значению. Экранирует значения |
| stripslashes | Применяет функцию PHP stripslashes к значению. Удаляет экранированные кавычки |
| capitalize | Преобразует строку в верхний регистр, применяя функцию PHP ucwords к значению |
| lower | Преобразует строку в нижний регистр |
| upper | Преобразует строку в верхний регистр |
| length | Подсчитывает длину строки или количество элементов в массиве или объекте |
| nl2br | Заменяет переводы строк \n на разрывы строк (<br />). Использует функцию PHP nl2br |
| sort | Сортирует массив, используя функцию PHP asort |
| keys | Возвращает ключи массива, используя array_keys |
| join | Объединяет части массива с помощью разделителя join |
| format | Форматирует строку, используя sprintf. |
| json_encode | Преобразует значение в его представление JSON |
| json_decode | Преобразует значение из его представления JSON в PHP представление |
| abs | Применяет функцию PHP abs к значению. |
| url_encode | Применяет функцию PHP urlencode к значению |
| default | Устанавливает значение по умолчанию в случае, если оцениваемое выражение пустое (не задано или вычисляется как ложное значение) |
| convert_encoding | Преобразует строку из одного набора символов в другой |
Примеры:
{# e or escape filter #}
{{ "<h1>Hello<h1>"|e }}
{{ "<h1>Hello<h1>"|escape }}
{# trim filter #}
{{ " hello "|trim }}
{# striptags filter #}
{{ "<h1>Hello<h1>"|striptags }}
{# slashes filter #}
{{ "'this is a string'"|slashes }}
{# stripslashes filter #}
{{ "\'this is a string\'"|stripslashes }}
{# capitalize filter #}
{{ "hello"|capitalize }}
{# lower filter #}
{{ "HELLO"|lower }}
{# upper filter #}
{{ "hello"|upper }}
{# length filter #}
{{ "robots"|length }}
{{ [1, 2, 3]|length }}
{# nl2br filter #}
{{ "some\ntext"|nl2br }}
{# sort filter #}
{% set sorted=[3, 1, 2]|sort %}
{# keys filter #}
{% set keys=['first': 1, 'second': 2, 'third': 3]|keys %}
{# json_encode filter #}
{% robots|json_encode %}
{# json_decode filter #}
{% set decoded='{"one":1,"two":2,"three":3}'|json_decode %}
{# url_encode filter #}
{{ post.permanent_link|url_encode }}
{# convert_encoding filter #}
{{ "désolé"|convert_encoding('utf8', 'latin1') }}
Комментарии
Комментарии также можно добавлять в шаблон, используя разделители {# ... #}. Весь текст внутри них игнорируется в конечном выводе:
{# note: this is a comment
{% set price = 100; %}
#}
Список управляющих структур
Volt предоставляет набор базовых, но мощных управляющих структур для использования в шаблонах:
For
Проходим по каждому элементу последовательности. Следующий пример демонстрирует, как пройти по набору «роботов» и вывести его/её имя:
<h1>Robots</h1>
<ul>
{% for robot in robots %}
<li>{{ robot.name|e }}</li>
{% endfor %}
</ul>
Циклы for также могут быть вложенными:
<h1>Robots</h1>
{% for robot in robots %}
{% for part in robot.parts %}
Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
{% endfor %}
{% endfor %}
Вы можете получить ключи элементов, как и в PHP, используя следующий синтаксис:
{% set numbers = ['one': 1, 'two': 2, 'three': 3] %}
{% for name, value in numbers %}
Name: {{ name }} Value: {{ value }}
{% endfor %}
Операция проверки «if» может быть опционально задана:
{% set numbers = ['one': 1, 'two': 2, 'three': 3] %}
{% for value in numbers if value < 2 %}
Value: {{ value }}
{% endfor %}
{% for name, value in numbers if name != 'two' %}
Name: {{ name }} Value: {{ value }}
{% endfor %}
Если внутри цикла for определён блок ‘else’, он будет выполнен, если выражение в итераторе не приведет ни к одной итерации:
<h1>Robots</h1>
{% for robot in robots %}
Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
{% else %}
There are no robots to show
{% endfor %}
Альтернативный синтаксис:
<h1>Robots</h1>
{% for robot in robots %}
Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
{% elsefor %}
There are no robots to show
{% endfor %}
Управляющие элементы цикла
Инструкции ‘break’ и ‘continue’ могут быть использованы для выхода из цикла или принудительной итерации в текущем блоке:
{# skip the even robots #}
{% for index, robot in robots %}
{% if index is even %}
{% continue %}
{% endif %}
...
{% endfor %}
{# exit the foreach on the first even robot #}
{% for index, robot in robots %}
{% if index is even %}
{% break %}
{% endif %}
...
{% endfor %}
If
Как и в PHP, оператор «if» проверяет, вычисляется ли выражение как истинное или ложное:
<h1>Cyborg Robots</h1>
<ul>
{% for robot in robots %}
{% if robot.type == "cyborg" %}
<li>{{ robot.name|e }}</li>
{% endif %}
{% endfor %}
</ul>
Также поддерживается блок else:
<h1>Robots</h1>
<ul>
{% for robot in robots %}
{% if robot.type == "cyborg" %}
<li>{{ robot.name|e }}</li>
{% else %}
<li>{{ robot.name|e }} (not a cyborg)</li>
{% endif %}
{% endfor %}
</ul>
Управляющая структура ‘elseif’ может использоваться вместе с if для эмуляции блока ‘switch’:
{% if robot.type == "cyborg" %}
Robot is a cyborg
{% elseif robot.type == "virtual" %}
Robot is virtual
{% elseif robot.type == "mechanical" %}
Robot is mechanical
{% endif %}
Контекст цикла
В циклах for доступна специальная переменная, предоставляющая информацию о
| Переменная | Описание |
|---|---|
| loop.index | Текущая итерация цикла. (Индексируется с 1) |
| loop.index0 | Текущая итерация цикла. (Индексируется с 0) |
| loop.revindex | Количество итераций от конца цикла (индексируется с 1) |
| loop.revindex0 | Количество итераций от конца цикла (индексируется с 0) |
| loop.first | Истина, если на первой итерации. |
| loop.last | Истина, если на последней итерации. |
| loop.length | Количество элементов для итерации |
{% for robot in robots %}
{% if loop.first %}
<table>
<tr>
<th>#</th>
<th>Id</th>
<th>Name</th>
</tr>
{% endif %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ robot.id }}</td>
<td>{{ robot.name }}</td>
</tr>
{% if loop.last %}
</table>
{% endif %}
{% endfor %}
Присваивания
Переменные могут быть изменены в шаблоне с помощью инструкции «set»:
{% set fruits = ['Apple', 'Banana', 'Orange'] %}
{% set name = robot.name %}
Разрешены несколько присваиваний в одной инструкции:
{% set fruits = ['Apple', 'Banana', 'Orange'], name = robot.name, active = true %}
Кроме того, вы можете использовать составные операторы присваивания:
{% set price += 100.00 %}
{% set age *= 5 %}
Доступны следующие операторы:
| Оператор | Описание |
|---|---|
| = | Стандартное присваивание |
| += | Присваивание с добавлением |
| -= | Присваивание с вычитанием |
| *= | Присваивание с умножением |
| /= | Присваивание с делением |
Выражения
Volt предоставляет базовый набор поддержки выражений, включая литералы и общие операторы.
Выражение можно оценить и вывести, используя разделители '{{‘ и ‘}}’:
{{ (1 + 1) * 2 }}
Если выражение нужно оценить без вывода, можно использовать оператор ‘do’:
{% do (1 + 1) * 2 %}
Литералы
Поддерживаются следующие литералы:
| Фильтр | Описание |
|---|---|
| “this is a string” | Текст в двойных или одинарных кавычках обрабатывается как строка |
| 100.25 | Числа с десятичной частью обрабатываются как double/float |
| 100 | Числа без десятичной части обрабатываются как целые числа |
| false | Постоянная «false» — это логическое значение ложь |
| true | Постоянная «true» — это логическое значение истина |
| null | Постоянная «null» — это значение Null |
Массивы
Используя PHP 5.3, 5.4 или 5.5, вы можете создавать массивы, заключив список значений в квадратные скобки:
{# Simple array #}
{{ ['Apple', 'Banana', 'Orange'] }}
{# Other simple array #}
{{ ['Apple', 1, 2.5, false, null] }}
{# Multi-Dimensional array #}
{{ [[1, 2], [3, 4], [5, 6]] }}
{# Hash-style array #}
{{ ['first': 1, 'second': 4/2, 'third': '3'] }}
Для определения массивов или хешей также можно использовать фигурные скобки:
{% set myArray = {'Apple', 'Banana', 'Orange'} %}
{% set myHash = {'first': 1, 'second': 4/2, 'third': '3'} %}
Математические операции
Вы можете выполнять вычисления в шаблонах, используя следующие операторы:
| Оператор | Описание |
|---|---|
| + | Выполняет операцию сложения. {{ 2 + 3 }} возвращает 5 |
| - | Выполняет операцию вычитания {{ 2 - 3 }} возвращает -1 |
| * | Выполняет операцию умножения {{ 2 * 3 }} возвращает 6 |
| / | Выполняет операцию деления {{ 10 / 2 }} возвращает 5 |
| % | Вычисляет остаток от деления целых чисел {{ 10 % 3 }} возвращает 1 |
Сравнения
Доступны следующие операторы сравнения:
| Оператор | Описание |
|---|---|
| == | Проверяет, равны ли оба операнда |
| != | Проверяет, не равны ли оба операнда |
| <> | Проверяет, не равны ли оба операнда |
| > | Проверяет, больше ли левый операнд, чем правый |
| < | Проверяет, меньше ли левый операнд, чем правый |
| <= | Проверяет, меньше или равно ли левый операнд правому |
| >= | Проверяет, больше или равно ли левый операнд правому |
| === | Проверяет, идентичны ли оба операнда |
| !== | Проверяет, не идентичны ли оба операнда |
Логические операторы
Логические операторы полезны при оценке выражений «if» для объединения нескольких проверок:
| Оператор | Описание |
|---|---|
| or | Возвращает true, если левый или правый операнд оценивается как true |
| and | Возвращает true, если оба левого и правого операнды оцениваются как true |
| not | Инвертирует выражение |
| ( expr ) | Скобки группируют выражения |
Другие операторы
Доступны следующие дополнительные операторы:
| Оператор | Описание |
|---|---|
| ~ | Конкатенирует оба операнда {{ “hello ” ~ “world” }} |
| | | Применяет фильтр к левому операнду, указанный в правом {{ “hello”|uppercase }} |
| .. | Создает диапазон {{ ‘a’..’z’ }} {{ 1..10 }} |
| is | То же, что и == (равно), также выполняет проверки |
| in | Проверяет, содержится ли выражение в других выражениях, если “a” in “abc” |
| is not | То же, что и != (не равно) |
| ‘a’ ? ‘b’ : ‘c’ | Тернарный оператор. Такой же, как тернарный оператор PHP |
| ++ | Инкрементирует значение |
| – | Декрементирует значение |
Следующий пример демонстрирует использование операторов:
{% set robots = ['Voltron', 'Astro Boy', 'Terminator', 'C3PO'] %}
{% for index in 0..robots|length %}
{% if robots[index] is defined %}
{{ "Name: " ~ robots[index] }}
{% endif %}
{% endfor %}
Тесты
Тесты могут использоваться для проверки, имеет ли переменная ожидаемое значение. Оператор «is» используется для выполнения проверок:
{% set robots = ['1': 'Voltron', '2': 'Astro Boy', '3': 'Terminator', '4': 'C3PO'] %}
{% for position, name in robots %}
{% if position is odd %}
{{ name }}
{% endif %}
{% endfor %}
В Volt доступны следующие встроенные тесты:
| Тест | Описание |
|---|---|
| defined | Проверяет, определена ли переменная (isset) |
| empty | Проверяет, пуста ли переменная |
| even | Проверяет, является ли число чётным |
| odd | Проверяет, является ли число нечётным |
| numeric | Проверяет, является ли значение числовым |
| scalar | Проверяет, является ли значение скалярным (не массивом и не объектом) |
| iterable | Проверяет, является ли значение итерируемым. Может быть пройдено по циклу «for» |
| divisibleby | Проверяет, делится ли значение на другое значение |
| sameas | Проверяет, идентичны ли значения |
| type | Проверяет, является ли значение заданного типа |
Дополнительные примеры:
{% if robot is defined %}
The robot variable is defined
{% endif %}
{% if robot is empty %}
The robot is null or isn't defined
{% endif %}
{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
{% if key is even %}
{{ name }}
{% endif %}
{% endfor %}
{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
{% if key is odd %}
{{ name }}
{% endif %}
{% endfor %}
{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 'third': 'Bender'] %}
{% if key is numeric %}
{{ name }}
{% endif %}
{% endfor %}
{% set robots = [1: 'Voltron', 2: 'Astroy Boy'] %}
{% if robots is iterable %}
{% for robot in robots %}
...
{% endfor %}
{% endif %}
{% set world = "hello" %}
{% if world is sameas("hello") %}
{{ "it's hello" }}
{% endif %}
{% set external = false %}
{% if external is type('boolean') %}
{{ "external is false or true" }}
{% endif %}
Макросы
Макросы могут использоваться для повторного использования логики в шаблоне, они действуют как функции PHP, могут принимать параметры и возвращать значения:
{%- macro related_bar(related_links) %}
<ul>
{%- for rellink in related_links %}
<li><a href="{{ url(link.url) }}" title="{{ link.title|striptags }}">{{ link.text }}</a></li>
{%- endfor %}
</ul>
{%- endmacro %}
{# Print related links #}
{{ related_bar(links) }}
<div>This is the content</div>
{# Print related links again #}
{{ related_bar(links) }}
При вызове макросов параметры могут передаваться по имени:
{%- macro error_messages(message, field, type) %}
<div>
<span class="error-type">{{ type }}</span>
<span class="error-field">{{ field }}</span>
<span class="error-message">{{ message }}</span>
</div>
{%- endmacro %}
{# Call the macro #}
{{ error_messages('type': 'Invalid', 'message': 'The name is invalid', 'field': 'name') }}
Макросы могут возвращать значения:
{%- macro my_input(name, class) %}
{% return text_field(name, 'class': class) %}
{%- endmacro %}
{# Call the macro #}
{{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}
И принимать необязательные параметры:
{%- macro my_input(name, class="input-text") %}
{% return text_field(name, 'class': class) %}
{%- endmacro %}
{# Call the macro #}
{{ '<p>' ~ my_input('name') ~ '</p>' }}
{{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}
Использование вспомогательных функций
Volt тесно интегрирован с Phalcon\Tag, поэтому легко использовать вспомогательные функции этого компонента в шаблоне Volt:
{{ javascript_include("js/jquery.js") }}
{{ form('products/save', 'method': 'post') }}
<label>Name</label>
{{ text_field("name", "size": 32) }}
<label>Type</label>
{{ select("type", productTypes, 'using': ['id', 'name']) }}
{{ submit_button('Send') }}
</form>
Генерируется следующий PHP:
<?php echo Phalcon\Tag::javascriptInclude("js/jquery.js") ?>
<?php echo Phalcon\Tag::form(array('products/save', 'method' => 'post')); ?>
<label>Name</label>
<?php echo Phalcon\Tag::textField(array('name', 'size' => 32)); ?>
<label>Type</label>
<?php echo Phalcon\Tag::select(array('type', $productTypes, 'using' => array('id', 'name'))); ?>
<?php echo Phalcon\Tag::submitButton('Send'); ?>
</form>
Для вызова вспомогательной функции Phalcon\Tag вам нужно просто вызвать её с несвернутым именем метода:
| Метод | Функция Volt |
|---|---|
| Phalcon\Tag::linkTo | link_to |
| Phalcon\Tag::textField | text_field |
| Phalcon\Tag::passwordField | password_field |
| Phalcon\Tag::hiddenField | hidden_field |
| Phalcon\Tag::fileField | file_field |
| Phalcon\Tag::checkField | check_field |
| Phalcon\Tag::radioField | radio_field |
| Phalcon\Tag::dateField | date_field |
| Phalcon\Tag::emailField | email_field |
| Phalcon\Tag::numberField | number_field |
| Phalcon\Tag::submitButton | submit_button |
| Phalcon\Tag::selectStatic | select_static |
| Phalcon\Tag::select | select |
| Phalcon\Tag::textArea | text_area |
| Phalcon\Tag::form | form |
| Phalcon\Tag::endForm | end_form |
| Phalcon\Tag::getTitle | get_title |
| Phalcon\Tag::stylesheetLink | stylesheet_link |
| Phalcon\Tag::javascriptInclude | javascript_include |
| Phalcon\Tag::image | image |
| Phalcon\Tag::friendlyTitle | friendly_title |
Функции
В Volt доступны следующие встроенные функции:
| Имя | Описание |
|---|---|
| content | Включает содержимое, сгенерированное на предыдущем этапе рендеринга |
| get_content | То же, что и ‘content’ |
| partial | Динамически загружает частичный вид в текущий шаблон |
| super | Отображает содержимое родительского блока |
| time | Вызывает функцию PHP с таким же именем |
| date | Вызывает функцию PHP с таким же именем |
| dump | Вызывает функцию PHP ‘var_dump’ |
| version | Возвращает текущую версию фреймворка |
| constant | Читает константу PHP |
| url | Генерирует URL с помощью сервиса ‘url’ |
Интеграция с представлением
Также Volt интегрирован с Phalcon\Mvc\View, вы можете работать с иерархией представлений и включать частичные представления:
{{ content() }}
<!-- Simple include of a partial -->
<div id="footer">{{ partial("partials/footer") }}</div>
<!-- Passing extra variables -->
<div id="footer">{{ partial("partials/footer", ['links': $links]) }}</div>
Частичное представление включается во время выполнения, Volt также предоставляет «include», это компилирует содержимое представления и возвращает его содержимое как часть включённого представления:
{# Simple include of a partial #}
<div id="footer">{% include "partials/footer" %}</div>
{# Passing extra variables #}
<div id="footer">{% include "partials/footer" with ['links': links] %}</div>
Включение
«include» имеет специальное поведение, которое поможет улучшить производительность при использовании Volt. Если вы указываете расширение при включении файла, и оно существует при компиляции шаблона, Volt может встроить содержимое шаблона в родительский шаблон, где он включен. Шаблоны не встраиваются, если с «include» переданы переменные с «with»:
{# The contents of 'partials/footer.volt' is compiled and inlined #}
<div id="footer">{% include "partials/footer.volt" %}</div>
Наследование шаблонов
С помощью наследования шаблонов можно создавать базовые шаблоны, которые могут быть расширены другими шаблонами, что позволяет повторно использовать код. Базовый шаблон определяет *блоки*, которые могут быть переопределены дочерним шаблоном. Предположим, что у нас есть следующий базовый шаблон:
{# templates/base.volt #}
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
{% endblock %}
<title>{% block title %}{% endblock %} - My Webpage</title>
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}© Copyright 2012, All rights reserved.{% endblock %}
</div>
</body>
</html>
Из другого шаблона мы можем расширить базовый шаблон, заменив блоки:
{% extends "templates/base.volt" %}
{% block title %}Index{% endblock %}
{% block head %}<style type="text/css">.important { color: #336699; }</style>{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">Welcome on my awesome homepage.</p>
{% endblock %}
Не все блоки должны быть заменены в дочернем шаблоне, только те, которые необходимы. Конечный вывод будет следующим:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">.important { color: #336699; }</style>
<title>Index - My Webpage</title>
</head>
<body>
<div id="content">
<h1>Index</h1>
<p class="important">Welcome on my awesome homepage.</p>
</div>
<div id="footer">
© Copyright 2012, All rights reserved.
</div>
</body>
</html>
Многократное наследование
Расширенные шаблоны могут расширять другие шаблоны. Следующий пример демонстрирует это:
{# main.volt #}
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
Шаблон «layout.volt» расширяет «main.volt»
{# layout.volt #}
{% extends "main.volt" %}
{% block content %}
<h1>Table of contents</h1>
{% endblock %}
И наконец, представление, которое расширяет «layout.volt»:
{# index.volt #}
{% extends "layout.volt" %}
{% block content %}
{{ super() }}
<ul>
<li>Some option</li>
<li>Some other option</li>
</ul>
{% endblock %}
Рендеринг «index.volt» даёт:
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Table of contents</h1>
<ul>
<li>Some option</li>
<li>Some other option</li>
</ul>
</body>
</html>
Обратите внимание на вызов функции «super()». С помощью этой функции можно отобразить содержимое родительского блока.
В качестве шаблонов пути, установленные на «extends», являются относительными путями в текущем каталоге представлений (т. е. app/views/).
По умолчанию и по соображениям производительности Volt проверяет изменения только в дочерних шаблонах, чтобы определить необходимость повторной компиляции в обычный PHP-код. Поэтому рекомендуется инициализировать Volt с параметром «compileAlways» => true. Таким образом, шаблоны всегда компилируются с учетом изменений в родительских шаблонах.
Режим автоматической экранизации
Вы можете включить автоматическую экранизацию всех переменных, напечатанных в блоке, используя режим автоматической экранизации:
Manually escaped: {{ robot.name|e }}
{% autoescape true %}
Autoescaped: {{ robot.name }}
{% autoescape false %}
No Autoescaped: {{ robot.name }}
{% endautoescape %}
{% endautoescape %}
Настройка движка Volt
Volt можно настроить для изменения его поведения по умолчанию. Следующий пример демонстрирует как это сделать:
use Phalcon\Mvc\View,
Phalcon\Mvc\View\Engine\Volt;
//Register Volt as a service
$di->set('voltService', function($view, $di) {
$volt = new Volt($view, $di);
$volt->setOptions(array(
"compiledPath" => "../app/compiled-templates/",
"compiledExtension" => ".compiled"
));
return $volt;
});
//Register Volt as template engine
$di->set('view', function() {
$view = new View();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
".volt" => 'voltService'
));
return $view;
});
Если вы не хотите повторно использовать Volt как сервис, вы можете передать анонимную функцию для регистрации движка вместо имени сервиса:
//Register Volt as template engine with an anonymous function
$di->set('view', function() {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
".volt" => function($view, $di) {
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
//set some options here
return $volt;
}
));
return $view;
});
Доступны следующие параметры в Volt:
| Параметр | Описание | Значение по умолчанию |
|---|---|---|
| compiledPath | Путь для записи скомпилированных PHP-шаблонов | ./ |
| compiledExtension | Дополнительное расширение, добавляемое к скомпилированному PHP-файлу | .php |
| compiledSeparator | Volt заменяет символы разделителей каталогов / и \ на этот разделитель, чтобы создать один файл в каталоге компиляции | %% |
| stat | Необходимо ли Phalcon проверять наличие различий между файлом шаблона и его скомпилированным эквивалентом | true |
| compileAlways | Указать Volt, нужно ли компилировать шаблоны в каждом запросе или только при их изменении | false |
| prefix | Позволяет добавить префикс к шаблонам в пути компиляции | null |
Путь компиляции генерируется в соответствии с вышеуказанными параметрами. Если разработчик хочет полную свободу в определении пути компиляции, можно использовать анонимную функцию, которая получает относительный путь к шаблону в каталоге представлений. Следующие примеры показывают, как динамически изменить путь компиляции:
// Just append the .php extension to the template path
// leaving the compiled templates in the same directory
$volt->setOptions(array(
'compiledPath' => function($templatePath) {
return $templatePath . '.php';
}
));
// Recursively create the same structure in another directory
$volt->setOptions(array(
'compiledPath' => function($templatePath) {
$dirName = dirname($templatePath);
if (!is_dir('cache/' . $dirName)) {
mkdir('cache/' . $dirName);
}
return 'cache/' . $dirName . '/'. $templatePath . '.php';
}
));
Расширение Volt
В отличие от других движков шаблонов, сам Volt не требуется для выполнения скомпилированных шаблонов. После компиляции шаблонов зависимость от Volt отпадает. Учитывая независимость от производительности, Volt действует только как компилятор для PHP-шаблонов.
Компилятор Volt позволяет расширять его, добавляя новые функции, проверки или фильтры к существующим.
Функции
Функции работают как обычные PHP-функции; требуется допустимое имя строки в качестве имени функции. Функции можно добавить, используя две стратегии: возвращение простой строки или использование анонимной функции. В обоих случаях требуется, чтобы выбранная стратегия возвращала допустимое выражение PHP-строки:
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
$compiler = $volt->getCompiler();
//This binds the function name 'shuffle' in Volt to the PHP function 'str_shuffle'
$compiler->addFunction('shuffle', 'str_shuffle');
Регистрация функции с помощью анонимной функции. В этом случае мы используем $resolvedArgs для передачи аргументов точно так, как они были переданы в аргументы:
$compiler->addFunction('widget', function($resolvedArgs, $exprArgs) {
return 'MyLibrary\Widgets::get(' . $resolvedArgs . ')';
});
Обработка аргументов независимо и без разрешения:
$compiler->addFunction('repeat', function($resolvedArgs, $exprArgs) use ($compiler) {
//Resolve the first argument
$firstArgument = $compiler->expression($exprArgs[0]['expr']);
//Checks if the second argument was passed
if (isset($exprArgs[1])) {
$secondArgument = $compiler->expression($exprArgs[1]['expr']);
} else {
//Use '10' as default
$secondArgument = '10';
}
return 'str_repeat(' . $firstArgument . ', ' . $secondArgument . ')';
});
Генерация кода на основе доступности некоторых функций:
$compiler->addFunction('contains_text', function($resolvedArgs, $exprArgs) {
if (function_exists('mb_stripos')) {
return 'mb_stripos(' . $resolvedArgs . ')';
} else {
return 'stripos(' . $resolvedArgs . ')';
}
});
Встроенные функции можно переопределить, добавив функцию с её именем:
//Replace built-in function dump
$compiler->addFunction('dump', 'print_r');
Фильтры
Фильтр имеет следующий вид в шаблоне: leftExpr|name(optional-args). Добавление новых фильтров аналогично добавлению функций:
//This creates a filter 'hash' that uses the PHP function 'md5'
$compiler->addFilter('hash', 'md5');
$compiler->addFilter('int', function($resolvedArgs, $exprArgs) {
return 'intval(' . $resolvedArgs . ')';
});
Встроенные фильтры можно переопределить, добавив функцию с её именем:
//Replace built-in filter 'capitalize'
$compiler->addFilter('capitalize', 'lcfirst');
Расширения
С помощью расширений разработчик получает большую гибкость для расширения движка шаблонов и переопределения компиляции определённой инструкции, изменения поведения выражения или оператора, добавления функций/фильтров и т. д.
Расширение — это класс, который реализует события, вызываемые Volt, как метод класса.
Например, нижеприведённый класс позволяет использовать любые PHP-функции в Volt:
class PhpFunctionExtension
{
/**
* This method is called on any attempt to compile a function call
*/
public function compileFunction($name, $arguments)
{
if (function_exists($name)) {
return $name . '('. $arguments . ')';
}
}
}
Вышеприведённый класс реализует метод «compileFunction», который вызывается перед любой попыткой скомпилировать вызов функции в любом шаблоне. Цель расширения — проверить, является ли компилируемая функция PHP-функцией, позволяя вызывать её из шаблона. События в расширениях должны возвращать допустимый PHP-код, который будет использоваться в качестве результата компиляции вместо генерируемого Volt. Если событие не возвращает строку, компиляция выполняется с помощью поведения по умолчанию, предоставляемого движком.
Доступны следующие события компиляции для реализации в расширениях:
| Событие/Метод | Описание |
|---|---|
| compileFunction | Вызывается перед попыткой компиляции вызова любой функции в шаблоне |
| compileFilter | Вызывается перед попыткой компиляции вызова любого фильтра в шаблоне |
| resolveExpression | Вызывается перед попыткой компиляции любого выражения. Это позволяет разработчику переопределять операторы |
| compileStatement | Вызывается перед попыткой компиляции любого выражения. Это позволяет разработчику переопределять любые операторы |
Расширения Volt должны быть зарегистрированы в компиляторе, делая их доступными во время компиляции:
//Register the extension in the compiler $compiler->addExtension(new PhpFunctionExtension());
Кэширование фрагментов представлений
С Volt легко кэшировать фрагменты представлений. Такое кэширование повышает производительность, предотвращая выполнение содержимого блока PHP каждый раз при отображении представления:
{% cache "sidebar" %}
<!-- generate this content is slow so we are going to cache it -->
{% endcache %}
Установка определённого количества секунд:
{# cache the sidebar by 1 hour #}
{% cache "sidebar" 3600 %}
<!-- generate this content is slow so we are going to cache it -->
{% endcache %}
В качестве ключа кэша может использоваться любое допустимое выражение:
{% cache ("article-" ~ post.id) 3600 %}
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
{% endcache %}
Кэширование выполняется компонентом Phalcon\Cache через компонент представления. Подробнее об этой интеграции можно узнать в разделе «Кэширование фрагментов представлений».
Ввод сервисов в шаблон
Если для Volt доступен контейнер сервисов (DI), вы можете использовать сервисы, просто обратившись к имени сервиса в шаблоне:
{# Inject the 'flash' service #}
<div id="messages">{{ flash.output() }}</div>
{# Inject the 'security' service #}
<input type="hidden" name="token" value="{{ security.getToken() }}">
Компонент автономного режима
Использование Volt в автономном режиме можно продемонстрировать ниже:
//Create a compiler
$compiler = new \Phalcon\Mvc\View\Engine\Volt\Compiler();
//Optionally add some options
$compiler->setOptions(array(
//...
));
//Compile a template string returning PHP code
echo $compiler->compileString('{{ "hello" }}');
//Compile a template in a file specifying the destination file
$compiler->compileFile('layouts/main.volt', 'cache/layouts/main.volt.php');
//Compile a template in a file based on the options passed to the compiler
$compiler->compile('layouts/main.volt');
//Require the compiled templated (optional)
require $compiler->getCompiledTemplatePath();
Внешние ресурсы
- Доступно расширение для Sublime/Textmate здесь
- Album-O-Rama — это пример приложения, использующего Volt в качестве движка шаблонов, [Github]
- Наш веб-сайт работает с использованием Volt в качестве движка шаблонов, [Github]
- Phosphorum, форум Phalcon, также использует Volt, [Github]
- Vökuró, — ещё одно примерное приложение, использующее Volt, [Github]
© 2011–2016 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/2.0.0/reference/volt.html