Volt: шаблонизатор

Volt-это сверхбыстрый и удобный для дизайнеров язык шаблонов, написанный на C для PHP. Он предоставляет вам набор помощников для написания представлений простым способом. Вольт сильно интегрирован с другими компонентами 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 в компоненте view, используя новое расширение или повторно используя стандарт .phtml:

<?php

use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;

// Регистрация Volt как сервиса
$di->set(
    'voltService',
    function ($view, $di) {
        $volt = new Volt($view, $di);

        $volt->setOptions(
            [
                'compiledPath'      => '../app/compiled-templates/',
                'compiledExtension' => '.compiled',
            ]
        );

        return $volt;
    }
);

// Регистрация Volt как шаблонизатора
$di->set(
    'view',
    function () {
        $view = new View();

        $view->setViewsDir('../app/views/');

        $view->registerEngines(
            [
                '.volt' => 'voltService',
            ]
        );

        return $view;
    }
);

Используя стандартное расширение .phtml:

<?php

$view->registerEngines(
    [
        '.phtml' => 'voltService',
    ]
);

Вам не нужно указывать службу Volt в DI; вы также можете использовать двигатель Volt с настройками по умолчанию:

<?php

$view->registerEngines(
    [
        '.volt' => Phalcon\Mvc\View\Engine\Volt::class,
    ]
);

Если вы не хотите повторно использовать Volt как службу, вы можете передать анонимную функцию для регистрации движка вместо имени службы:

<?php

use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;

// Регистрировать Volt как механизм шаблонов с анонимной функцией
$di->set(
    'view',
    function () {
        $view = new View();

        $view->setViewsDir('../app/views/');

        $view->registerEngines(
            [
                '.volt' => function ($view, $di) {
                    $volt = new Volt($view, $di);

                    // Set some options here

                    return $volt;
                }
            ]
        );

        return $view;
    }
);

Следующие параметры доступны в Volt:

ПараметрОписаниеПо умолчанию
autoescape Включает глобальный автозапуск HTML false
compileAlways Указать Volt, - шаблоны должны быть скомпилированы в каждом запросе или только когда они меняются false
compiledExtension Дополнительное расширение, добавленное к скомпилированному файлу PHP .php
compiledPath Путь для записи, в котором будут храниться скомпилированные шаблоны PHP ./
compiledSeparator Volt заменяет разделители каталогов / и \ этим разделителем, чтобы создать один файл в скомпилированном каталоге %%
prefix Позволяет добавлять префикс к шаблонам в пути компиляции null
stat Должен ли Phalcon проверить, существуют ли различия между файлом шаблона и его скомпилированным путем true

Путь компиляции создается в соответствии с приведенными выше параметрами, если разработчик хочет, чтобы полная свобода определяла путь компиляции, для его создания можно использовать анонимную функцию, эта функция получает относительный путь к шаблону в каталоге представлений. В следующих примерах показано, как динамически изменять путь компиляции:

<?php

// Просто добавьте расширение .php к пути шаблона,
// оставляя скомпилированные шаблоны в том же каталоге
$volt->setOptions(
    [
        'compiledPath' => function ($templatePath) {
            return $templatePath . '.php';
        }
    ]
);

// Рекурсивно создать ту же структуру в другом каталоге
$volt->setOptions(
    [
        'compiledPath' => function ($templatePath) {
            $dirName = dirname($templatePath);

            if (!is_dir('cache/' . $dirName)) {
                mkdir('cache/' . $dirName , 0777 , true);
            }

            return 'cache/' . $dirName . '/'. $templatePath . '.php';
        }
    ]
);

Основное использование

Представление состоит из вольт-кода, PHP и HTML. Для входа в режим Volt доступен набор специальных разделителей. {% ...%} используется для выполнения таких операторов, как for-loops или assign values, и {{...}}, выводит результат выражения в шаблон.

Ниже приведен минимальный шаблон, который иллюстрирует несколько основ:

{# app/views/posts/show.phtml #}
<!DOCTYPE html>
<html>
    <head>
        <title>{{ title }} - Пример блога</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, вы можете передавать переменные из контроллера в представления. В приведенном выше примере в представление были переданы четыре переменные: show_navigation, menu, title и post:

<?php

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    public function showAction()
    {
        $post = Post::findFirst();
        $menu = Menu::findFirst();

        $this->view->show_navigation = true;
        $this->view->menu            = $menu;
        $this->view->title           = $post->title;
        $this->view->post            = $post;

        // Or...

        $this->view->setVar('show_navigation', true);
        $this->view->setVar('menu',            $menu);
        $this->view->setVar('title',           $post->title);
        $this->view->setVar('post',            $post);
    }
}

Переменные

Переменные объекта могут иметь атрибуты, к которым можно получить доступ, используя синтаксис: foo.bar. Если вы передаете массивы, вы должны использовать синтаксис квадратных скобок: foo['bar']

{{ post.title }} {# for $post->title #}
{{ post['title'] }} {# for $post['title'] #}

Фильтры

Переменные можно форматировать или изменять с помощью фильтров. Pipe оператор | используется для применения фильтров к переменным:

{{ post.title|e }}
{{ post.content|striptags }}
{{ name|capitalize|trim }}

Ниже приведен список доступных встроенных фильтров в Volt:

ФильтрОписание
abs Применяет функцию PHP abs к значению.
capitalize Заглавная строка, применяя функцию PHP ucwords к значению
convert_encoding Преобразует строку из одной кодировки в другую
default Устанавливает значение по умолчанию в том случае, если вычисленное выражение пусто (не задано или не оценивается как ложное значение)
e Применяет Phalcon\Escaper->escapeHtml() к значению
escape Применяет Phalcon\Escaper->escapeHtml() к значению
escape_attr Применяет Phalcon\Escaper->escapeHtmlAttr() к значению
escape_css Применяет Phalcon\Escaper->escapeCss() к значению
escape_js Применяет Phalcon\Escaper->escapeJs() к значению
format Форматирует строку, используя sprintf.
json_encode Преобразует значение в его представление JSON
json_decode Преобразует значение из представления JSON в представление PHP
join Присоединяется к частям массива с помощью разделителя join
keys Возвращает ключи массива с помощью array_keys
left_trim Применяет PHP-функцию ltrim к значению. Удаление лишних пробелов
length Подсчитывает длину строки или количество элементов в массиве или объекте
lower Изменяет регистр строки на нижний регистр
nl2br Изменения строки \n на строки (<br />). Использует PHP функцию nl2br
right_trim Применяет PHP-функцию rtrim к значению. Удаление лишних пробелов
sort Сортировка массива с помощью функции PHP asort
stripslashes Применяет к значению PHP-функцию stripslashes. Удаление экранированных кавычек
striptags Применяет функцию PHP striptags к значению. Удаление HTML-тегов
trim Применяет PHP-функцию trim к значению. Удаление лишних пробелов
upper Изменяет регистр строки на верхний регистр
url_encode Применяет PHP-функцию urlencode к значению

Примеры:

{# 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 %}

{# join filter #}
{% set joined = 'a'..'z'|join(',') %}

{# format filter #}
{{ 'My real name is %s'|format(name) }}

{# json_encode filter #}
{% set encoded = 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') }}

Комментарии

Комментарии также могут быть добавлены в шаблон с использованием разделителей {# ... #}. Весь текст внутри них просто игнорируется в выходном потоке:

{# примечание: это комментарий
    {% set price = 100; %}
#}

Список структур управления

Volt предоставляет набор базовых, но мощных структур управления для использования в шаблонах:

For

Цикл над каждым элементом в последовательности. В следующем примере показано, как пройти набор 'robots' и напечатать его / ее имя:

<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 %}

Вы можете получить ключи keys элементов, как в 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 %}

Если else определен внутри for, он будет выполнен, если выражение в итераторе приведет к нулевым итерациям:

<h1>Robots</h1>
{% for robot in robots %}
    Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br />
{% else %}
    Нет роботов, чтобы показать
{% endfor %}

Альтернативный синтаксис:

<h1>Robots</h1>
{% for robot in robots %}
    Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br />
{% elsefor %}
    Нет роботов, чтобы показать
{% 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 проверяет, если выражение оценивается как true или false:

<h1>Роботы-киборги</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 %}

Switch

Альтернативой оператору if является switch, позволяющий создавать логические пути выполнения в приложении:

{% switch foo %}
    {% case 0 %}
    {% case 1 %}
    {% case 2 %}
        "foo" is less than 3 but not negative
        {% break %}
    {% case 3 %}
        "foo" is 3
        {% break %}
    {% default %}
        "foo" is {{ foo }}
{% endswitch %}

Оператор switch выполняет оператор за оператором, поэтому оператор break необходим в некоторых случаях. Любой вывод (включая пробелы) между оператором switch и первым case приведет к синтаксической ошибке. Поэтому пустые строки и пробелы могут быть удалены, чтобы уменьшить количество ошибок см. здесь.

case без switch

{% case EXPRESSION %}

Будет бросать Fatal error: Uncaught Phalcon\Mvc\View\Exception: Unexpected CASE.

switch без endswitch

{% switch EXPRESSION %}
Will throw `Fatal error: Uncaught Phalcon\Mvc\View\Exception: Syntax error, unexpected EOF in ..., there is a 'switch' block without 'endswitch'`.

default без switch

{% default %}

Не выдаст ошибку, так как default является зарезервированным словом для таких фильтров, как {{ EXPRESSION | default(VALUE) }} , но в этом случае выражение выдаст только пустой символ '' .

вложенный switch

{% switch EXPRESSION %}
  {% switch EXPRESSION %}
  {% endswitch %}
{% endswitch %}

Будет бросать Fatal error: Uncaught Phalcon\Mvc\View\Exception: A nested switch detected. There is no nested switch-case statements support in ... on line ...

 switch без выражения

{% switch %}
  {% case EXPRESSION %}
      {% break %}
{% endswitch %}

Будет бросать Fatal error: Uncaught Phalcon\Mvc\View\Exception: Syntax error, unexpected token %} in ... on line ...

Контекст Цикла

Внутри цикла for доступна специальная переменная, предоставляющая информацию о

ПеременнаяОписание
loop.index Текущая итерация цикла. (1 проиндексированных)
loop.index0 Текущая итерация цикла. (0 проиндексированных)
loop.revindex Количество итераций с конца цикла (1 индексированная)
loop.revindex0 Количество итераций от конца цикла (0 индексированных)
loop.first True если в первой итерации.
loop.last True если в последней итерации.
loop.length Количество элементов перебора

Example:

{% 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 / floats
100 Числа без десятичной части обрабатываются как целые числа
false Константа 'false' - логическое значение false
true Константа 'true' - логическое  значение true
null Константа 'null' - значение Null

Массивы

Если вы используете PHP 5.3 или >= 5.4 можно создавать массивы, заключив список значений в квадратных скобках:

{# 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'} %}

Математика

Вычисления в шаблонах можно выполнять с помощью следующих операторов:

OperatorDescription
+ Выполнение операции сложения. {{ 2 + 3 }} возвращает 5
- Выполнение операции вычитания {{ 2 - 3 }} возвращает -1
* Выполнение операции умножения {{ 2 * 3 }} возвращает 6
/ Выполнение операции деления {{ 10 / 2 }} возвращает 5
% Вычислить остаток от деления целого числа {{ 10 % 3 }} возвращает 1

Сравнения

Доступны следующие операторы сравнения:

ОператорОписание
== Проверяет, равны ли оба операнда
!= Проверяет, не равны ли оба операнда
<> Проверяет, не равны ли оба операнда
> Проверяет, больше ли левый операнд, чем правый
< Проверяет, является ли левый операнд меньше правого
<= Проверяет, является ли левый операнд меньше или равен правому операнду
>= Проверяет, является ли левый операнд больше или равен правому операнду
=== Проверяет, идентичны ли оба операнда
!== Проверяет, не идентичны ли оба операнда

Логика

Логические операторы полезны при вычислении выражения if для объединения нескольких тестов:

OperatorDescription
or Возвращает true, если левый или правый операнд оценивается как истина
and Возвращает true, если левый и правый операнды оцениваются как true
not Отрицает выражение
( expr ) Выражения групп скобок

Остальные операторы

Дополнительные операторы доступны следующие операторы:

ОператорОписание
~ Объединяет оба операнда {{ 'hello ' ~ 'world' }}
| Применяет фильтр в правом операнде к левому {{ 'hello'|uppercase }}
.. Создает диапазон {{ 'a'..'z' }} {{ 1..10 }}
is То же, что == (равно), также выполняет тесты
in Чтобы проверить, содержится ли выражение в других выражениях if 'a' in 'abc'
is not То же самое != (не равный)
'a' ? 'b' : 'c' Тернарный оператор. То же, что и тернарный оператор PHP
++ Увеличивает значение на 1
-- Уменьшение значения на 1

В следующем примере показано, как использовать операторы:

{% 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:

TestDescription
defined Проверяет, определена ли переменная (isset())
divisibleby Проверяет, делится ли значение на другое
empty Проверяет, является ли переменная пустой
even Проверяет, является ли числовое значение четным
iterable Проверяет, является ли значение итерационным. Можно пройти с помощью оператора for
numeric Проверяет, является ли значение числовым
odd Проверяет, является ли числовое значение нечетным
sameas Проверяет, совпадает ли значение с другим значением
scalar Проверяет, является ли значение скалярным (не массивом или объектом)
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 'отображение списка ссылок на связанные темы' #}
{%- macro related_bar(related_links) %}
    <ul>
        {%- for link in related_links %}
            <li>
                <a href='{{ url(link.url) }}' title='{{ link.title|striptags }}'>
                    {{ link.text }}
                </a>
            </li>
        {%- endfor %}
    </ul>
{%- endmacro %}

{# Печать связанных ссылок #}
{{ related_bar(links) }}

<div>Это содержание</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 %}

{# Вызов макроса #}
{{ error_messages('type': 'Invalid', 'message': 'The name is invalid', 'field': 'name') }}

Макросы могут возвращать значения:

{%- macro my_input(name, class) %}
    {% return text_field(name, 'class': class) %}
{%- endmacro %}

{# Вызов макроса #}
{{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}

И получить дополнительные параметры:

{%- macro my_input(name, class='input-text') %}
    {% return text_field(name, 'class': class) %}
{%- endmacro %}

{# Вызов макроса #}
{{ '<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 for='name'>Name</label>
    {{ text_field('name', 'size': 32) }}

    <label for='type'>Type</label>
    {{ select('type', productTypes, 'using': ['id', 'name']) }}

    {{ submit_button('Send') }}

{{ end_form() }}

Генерируется следующий PHP:

<?php echo Phalcon\Tag::javascriptInclude('js/jquery.js') ?>

<?php echo Phalcon\Tag::form(array('products/save', 'method' => 'post')); ?>

    <label for='name'>Name</label>
    <?php echo Phalcon\Tag::textField(array('name', 'size' => 32)); ?>

    <label for='type'>Type</label>
    <?php echo Phalcon\Tag::select(array('type', $productTypes, 'using' => array('id', 'name'))); ?>

    <?php echo Phalcon\Tag::submitButton('Send'); ?>

{{ end_form() }}

Чтобы вызвать помощник Phalcon\Tag, вам нужно только вызвать некамелизованную версию метода:

МетодФункция Volt
Phalcon\Tag::checkField check_field
Phalcon\Tag::dateField date_field
Phalcon\Tag::emailField email_field
Phalcon\Tag::endForm end_form
Phalcon\Tag::fileField file_field
Phalcon\Tag::form form
Phalcon\Tag::friendlyTitle friendly_title
Phalcon\Tag::getTitle get_title
Phalcon\Tag::hiddenField hidden_field
Phalcon\Tag::image image
Phalcon\Tag::javascriptInclude javascript_include
Phalcon\Tag::linkTo link_to
Phalcon\Tag::numericField numeric_field
Phalcon\Tag::passwordField password_field
Phalcon\Tag::radioField radio_field
Phalcon\Tag::select select
Phalcon\Tag::selectStatic select_static
Phalcon\Tag::stylesheetLink stylesheet_link
Phalcon\Tag::submitButton submit_button
Phalcon\Tag::textArea text_area
Phalcon\Tag::textField text_field

Функции

Следующие встроенные функции доступны в 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() }}

<!-- Простое включение частичного  представления-->
<div id='footer'>{{ partial('partials/footer') }}</div>

<!-- Передача дополнительных переменных -->
<div id='footer'>{{ partial('partials/footer', ['links': links]) }}</div>

Часть включается во время выполнения, Volt также предоставляет include, это компилирует содержимое представления и возвращает его содержимое как часть представления, которое было включено:

{# Простое включение части #}
<div id='footer'>
    {% include 'partials/footer' %}
</div>

{# Передача дополнительных переменных #}
<div id='footer'>
    {% include 'partials/footer' with ['links': links] %}
</div>

Include

У include есть специальное поведение, которое поможет нам немного улучшить производительность при использовании Volt, если вы укажете расширение при включении файла, и оно существует, когда шаблон компилируется, Volt может встроить содержимое шаблона в родительский шаблон, где он включен. Шаблоны не являются встроенными, если include имеют переменные, переданные с with:

{# Содержимое 'partials/footer.volt' скомпилировано и встроено #}
<div id='footer'>
    {% include 'partials/footer.volt' %}
</div>

Partial против Include

При использовании функции partial или include помните о следующих моментах:

ТипОписание
partial позволяет включать шаблоны, выполненные в Volt и других шаблонизаторах
  позволяет передать выражение в виде переменной, позволяющей динамически включать содержимое другого представления
  лучше, если содержание, которое вы должны включать изменяется часто
includes копирует скомпилированное содержимое в представление, что повышает производительность
  только позволяет включать шаблоны, сделанные с помощью Volt
  требует наличия шаблона во время компиляции

Наследование Шаблонов

С помощью наследования шаблонов вы можете создавать базовые шаблоны, которые могут быть расширены другими шаблонами, позволяющими повторно использовать код. Базовый шаблон определяет блоки, которые могут быть переопределены дочерним шаблоном. Давайте представим, что у нас есть следующий базовый шаблон:

{# 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 2015, 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 2015, 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(). С помощью этой функции можно отобразить содержимое родительского блока. Как partials, путь, установленный в extends, является относительным путем в текущем каталоге представлений (т. е. app/views/).

Режим Autoescape

Вы можете включить авто-экранирование всех переменных, выведенных в блок, используя режим autoescape :

Manually escaped: {{ robot.name|e }}

{% autoescape true %}
    Autoescaped: {{ robot.name }}
    {% autoescape false %}
        No Autoescaped: {{ robot.name }}
    {% endautoescape %}
{% endautoescape %}

Расширение Volt

В отличие от других шаблонизаторов, Volt не требуется для запуска скомпилированных шаблонов. После того, как шаблоны скомпилированы нет никакой зависимости от Volt. Имея в виду независимость от производительности, Volt действует только как компилятор для шаблонов PHP.

Компилятор Volt позволяет расширить его, добавив больше функций, тестов или фильтров к существующим.

Функции

Функции действуют как обычные функции PHP, в качестве имени функции требуется допустимое имя строки. Функции можно добавлять с помощью двух стратегий, возвращая простую строку или используя анонимную функцию. Всегда требуется, чтобы выбранная стратегия возвращала допустимое строковое выражение PHP:

<?php

use Phalcon\Mvc\View\Engine\Volt;

$volt = new Volt($view, $di);

$compiler = $volt->getCompiler();

// Это связывает имя функции 'shuffle' в Volt с функцией PHP 'str_shuffle'
$compiler->addFunction('shuffle', 'str_shuffle');

Зарегистрируйте функцию с помощью анонимной функции. В этом случае мы используем $resolvedArgs для передачи аргументов точно так, как они были переданы в аргументах функции:

<?php

$compiler->addFunction(
    'widget',
    function ($resolvedArgs, $exprArgs) {
        return 'MyLibrary\Widgets::get(' . $resolvedArgs . ')';
    }
);

Обрабатывать аргументы независимо и неразрешено:

<?php

$compiler->addFunction(
    'repeat',
    function ($resolvedArgs, $exprArgs) use ($compiler) {
        // Разрешить первый аргумент
        $firstArgument = $compiler->expression($exprArgs[0]['expr']);

        // Проверяет, прошел ли второй аргумент
        if (isset($exprArgs[1])) {
            $secondArgument = $compiler->expression($exprArgs[1]['expr']);
        } else {
            // Используйте «10» по умолчанию
            $secondArgument = '10';
        }

        return 'str_repeat(' . $firstArgument . ', ' . $secondArgument . ')';
    }
);

Создание кода на основе доступности некоторых функций:

<?php

$compiler->addFunction(
    'contains_text',
    function ($resolvedArgs, $exprArgs) {
        if (function_exists('mb_stripos')) {
            return 'mb_stripos(' . $resolvedArgs . ')';
        } else {
            return 'stripos(' . $resolvedArgs . ')';
        }
    }
);

Встроенные функции можно переопределить, добавив функцию с именем:

<?php

// Заменить  встроенную функцию dump
$compiler->addFunction('dump', 'print_r');
 

Фильтры

Фильтр имеет следующую форму в шаблоне: leftExpr|name(optional-args). Добавление новых фильтров аналогично описанию функций:

<?php

// Это создает фильтр 'hash', который использует функцию PHP 'md5'
$compiler->addFilter('hash', 'md5');
<?php

$compiler->addFilter(
    'int',
    function ($resolvedArgs, $exprArgs) {
        return 'intval(' . $resolvedArgs . ')';
    }
);

Встроенные фильтры можно переопределить, добавив функцию с ее именем:

<?php

// Заменить встроенный фильтр 'capitalize'
$compiler->addFilter('capitalize', 'lcfirst');

Расширения

С помощью расширений разработчик имеет больше возможностей для расширения механизма шаблонов и переопределения компиляции конкретной инструкции, изменения поведения выражения или оператора, добавления функций/фильтров и т. д.

Расширение — это класс, реализующий события, инициированные Volt как метод самого себя. Например, класс, приведенный ниже, позволяет использовать любую функцию PHP в Volt:

<?php

class PhpFunctionExtension
{
    /**
     * Этот метод вызывается при любой попытке компиляции вызова функции
     */
    public function compileFunction($name, $arguments)
    {
        if (function_exists($name)) {
            return $name . '('. $arguments . ')';
        }
    }
}

Приведенный выше класс реализует метод compileFunction, который вызывается перед любой попыткой компиляции вызова функции в любом шаблоне. Цель расширения-проверить, является ли компилируемая функция PHP-функцией, позволяющей вызывать ее из шаблона. События в расширениях должны возвращать корректный PHP код, который будет использоваться в результате компиляции, а не сгенерированный Volt. Если событие не возвращает строку компиляции осуществляется с помощью поведения по умолчанию движка.

В расширениях доступны следующие события компиляции:

Событие/МетодОписание
compileFunction Срабатывает перед компиляцией любого вызова функции в шаблоне
compileFilter Срабатывает перед компиляцией любого вызова фильтра в шаблоне
resolveExpression Срабатывает перед компиляцией любого выражения. Это позволяет разработчику переопределять операторы
compileStatement Срабатывает перед компиляцией любого выражения. Это позволяет разработчику переопределить любой оператор

Расширения Volt должны быть зарегистрированы в компиляторе, что делает их доступными во время компиляции:

<?php

// Зарегистрировать расширение в компиляторе
$compiler->addExtension(
    new PhpFunctionExtension()
);

Кэширование фрагментов представления

С Volt легко кэшировать фрагменты. Это кэширование повышает производительность, предотвращая выполнение PHP содержимого блока при каждом отображении представления:

{% cache 'sidebar' %}
    <!-- генерировать этот контент медленно, поэтому мы будем кэшировать его -->
{% endcache %}

Установка определенного количества секунд:

{# кэш боковой панели на 1 час #}
{% cache 'sidebar' 3600 %}
    <!-- генерировать этот контент медленно, поэтому мы будем кэшировать его -->
{% endcache %}

В качестве ключа кэша можно использовать любое допустимое выражение:

{% cache ('article-' ~ post.id) 3600 %}

    <h1>{{ post.title }}</h1>

    <p>{{ post.content }}</p>

{% endcache %}

Кэширование осуществляется компонентом Phalcon\Cache через компонент view. Подробнее об этой интеграции см. В разделе кэширование фрагментов представления.

Внедрение служб в шаблон

Если контейнер служб (DI) доступен для Volt, можно использовать службы, получив доступ только к имени службы в шаблоне:

{# Впрыснуть службу 'flash' #}
<div id='messages'>{{ flash.output() }}</div>

{# Впрыснуть службу 'security' #}
<input type='hidden' name='token' value='{{ security.getToken() }}'>

Автономный компонент

Использование Volt в автономном режиме можно продемонстрировать ниже:

<?php

use Phalcon\Mvc\View\Engine\Volt\Compiler as VoltCompiler;

// Создать компилятор
$compiler = new VoltCompiler();

// При необходимости добавьте некоторые параметры
$compiler->setOptions(
    [
        // ...
    ]
);

// Скомпилировать строку шаблона, возвращающую PHP-код
echo $compiler->compileString(
    "{{ 'hello' }}"
);

// Скомпилировать шаблон в файле с указанием целевого файла
$compiler->compileFile(
    'layouts/main.volt',
    'cache/layouts/main.volt.php'
);

// Компилировать шаблон в файле на основе параметров, переданных компилятору
$compiler->compile(
    'layouts/main.volt'
);

// Требовать скомпилированные шаблонные (необязательные)
require $compiler->getCompiledTemplatePath();

Внешние ресурсы

  • Здесь доступен комплект для Sublime/Textmate
  • Album-O-Rama - пример приложения, использующего Volt в качестве механизма шаблонов, Github
  • Наш веб-сайт работает с использованием Volt в качестве механизма шаблонов Github
  • Phosphorum, форум Phalcon, также использует Volt, Github
  • Vökuró, является еще одним примером приложения, которое использует Volt, Github