Clone wiki

CMS / Синтаксис_шаблонного_интерпретатора

Начиная с версии Atom-M 2.3.4 можно подключать специальный шаблонный интерпретатор, который, по умолчанию, подключен в шаблонах. Как подключить его в другое место написано тут: Подключение шаблонизатора

Итак начнем.

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

Арифметические операции

На данный момент поддерживается:

  • "+" - сложение (или "~" если нужно сложить строки)

  • "-" - вычитание

  • "*" - умножение

  • "/" - деление

  • "%" - нахождение остатка

Переменные

Вся прелесть шаблонизатора заключается в том, что он может работать с контекстом, переданным ему из другой области (в нашем случае из PHP). Другими словами - мы можем использовать в шаблонах переменные, пришедшие к нам из PHP, не зная ничего о самом PHP вообще. Не правда ли это круто? Дело в том, что каждый шаблон имеет свой, заранее определенный контекст, например, шаблон list.html принимает массив новостей, хранящийся в переменной entities. Но кроме этого, он так же принимает и некий набор глобальных переменных (доступных в любом шаблоне), например, title, meta_description и т.д. Но как же нам вывести значение этих переменных в шаблон, чтобы они были видны пользователям? Очень просто:

<!-- Выводим заголовок -->
<title>{{ title }}</title>
<!-- Выводим описание -->
<meta name="description" content="{{ meta_description }}" />
<!-- просто какая-то переменная -->
{{ entity.some_var }}

Если попытаться вывести несуществующую переменную, то вместо нее выведется просто пустая строка, тоесть "ничего". Как это ни странно). Но при работе с циклами и уловными выражениями все может быть не так гладко, будьте осторожны.

Фильтры

Теперь мы знаем как выводить переменные, но переменные бывают разные и выводиться они тоже могут в разных местах. Зачастую необходимо эти переменные экранировать, точнее экранировать HTML сущности в них, чтобы пользователи увидели текст, как текст, а не как HTML код. Это кстати спасает от такой плохой штуки как XSS. И вот тут нам на помощь приходят фильтры. Использовать фильтры очень просто:

<!-- Переводим HTML в простой текст в заголовке -->
<title>{{ title|escape }}</title>

<!-- Пример фильтра с параметрами(разобьет текущий URL страницы на составляющие в виде элементов массива) -->
<title>{{ fps_request_url|split('/') }}</title>

Как видите, все предельно просто. Стоит заметить, что существует очень много разнообразных фильтров. И в отличие от функций они редактируют обьекты(переменные) или создают новые из других обьектов. Функции же в нашем шаблонизаторе предназначены для создания обьекта "с нуля", хотя и редактировать обьекты они тоже могут. Спрашивается, зачем же нужны фильтры если есть функции? Скажем так, просто для красоты и совместимости с twig-подобными шаблонизаторами.

Условные выражения(Оператор if)

Условные выражения позволяют делать проверку на содержимое метки и в зависимости от результата выводить тот или иной html.Тут все так же, как в любом языке программирования. Их синтаксис прост:

{% if <сравнительное значение> <знак сравнения> <сравнительное значение>  %}
Условное выражение истина(правильно)
{% else if <сравнительное значение> <знак сравнения> <сравнительное значение> %}
Второе условное выражение истина(правильно), а первое ложь(не правильно)
{% else %}
Оба условных выражения ложь(не правильно)
{% endif %}

("<>" – не входит в выражение)

  • <знак сравнения> - знак сравнения, может быть одним из:
    • == - нестрогое равенство.
    • != - нестрогое не равно.
    • >= - то что с лева больше или равно того что с права.
    • <= - то что с лева меньше или равно того что с права.
    • > - то что с лева больше того что с права.
    • < - то что с лева меньше того что с права.
    • in - проверка на вхождение, того что с лева в последовательность, того что с права.
    • not in - обратная процедура in проверка на не вхождение.
    • === - строгое равенство(Новое в Atom-M 5).
    • !== - строгое не равно(Новое в Atom-M 5).
  • <сравнительное значение> - значение, метка(Внутри конструкций {% %} записывается без {{ }}) или выражение для получения значения.

Стоит упомянуть, что шаблонизатор поддерживает даже несколько условий в одном выражении if. Для этого специально существует операторы объединения нескольких условий, состоящих из <сравнительное значение> <знак сравнения> <сравнительное значение> или только <сравнительное значение>(см.ниже): and - оператор вернет истину, если оба выражения, которые он соединяет, истинны. or - оператор вернет истину если хотя бы одно из выражений которые он соединяет истинно.

истина - в понятии логики программирования означет, что условие выполнилось. (например, 5 > 3 - истина, а 5 < 3 - ложь)

Также знайте, {% else %} и {% else if ... %} являются не обязательными атрибутами конструкции, Вы можете и не делать никаких действий, если первое условие оказалось ложным. И если вам всего лишь нужно проверить метку на существование(или выражение на истинность), то существует сокращенная запись:

{% if <название метки> %} Метка существует {% endif %}.

метка не существует, если она равна нулю(0), равна лжи(false) или пустой строке ('')

Такая запись эквивалентна: {% if <название метки> == 1 %} Метка существует {% endif %} т.к. оператор == является оператором нестрогого сравнения и приводит сравнительные значения к одному типу, поэтому при сравнении, например '',false,0,[], между собой они окажутся равными.

Пару примеров:

{% if entity.name == 'Petya' %}
    <p><span>{{ entity.name }}</span></p>
{% else %}
    <p>{{ entity.name }}</p>
{% endif %}

Тут мы выводим ник пользователя, но, если этот ник Petya, мы дополнительно заключаем его в тег span. Но бывают и такие ситуации когда нам не нужен блок else, например как в следующем примере.

{% if entity.views >= 100 and entity.id != 1 %}
    <div class="congratulation">{{ entity.title }}</div>
{% endif %}

Заметил что изменилось, кроме убранного блока else? Правильно - изменилось и само условие. Я сделал это для того, чтобы сразу продемонстрировать тебе еще одну возможность - множественные параметры. Если в первом примере было одно условие, то тут их сразу два(больше 100 просмотров и ID не должен быть 1). На самом деле условий может быть сколько угодно, но надо понимать, что накатай ты 100 условий в одном блоке, ты потом и сам не поймешь откуда ноги растут, так что тут следует быть осторожным или, правильнее сказать, эстетичным. Код должен легко читаться, запомни это и повторяй как мантру. А вот пример самого простого использования if(когда просто надо проверить, что переменная не пустая, тоесть не 0 и не false, и не пустая строка):

{% if entity.title %}
    <div class="congratulation">{{ entity.title }}</div>
{% endif %}

Циклы (Оператор for)

С помощью этого оператора мы сможем "проходить" по каждому элементу некоего массива. Чтобы стало понятнее что это такое, можно вспомнить пример с шаблоном list.html и его контекстом - масиивом записей (entities).Так как это массив, содержажий в себе множество записей, нам надо каким-то образом пройтись по каждой из них. и вот тут тебе должно стать понятно нафига этот for. А использовать его очень просто, возмем пример из предыдущего раздела и применим к каждой записи в нашем массиве:

{% for entity in entities %}
    <div>{{ entity.name1 }}</div>
    <div>{{ entity.name2 }}</div>
{% endfor %}

Как ты, наверное, заметил, for принимает два параметра (левый и правый) и разделилтель in. Так вот правый параметр - это исходные данные (наш массив), а левый - это новая переменная, которая образуется из каждого элемента массива. Это похоже на for в любом другом языке программирования. Проще говоря, мы выполняем код внутри блока for столько раз, сколько элементов у нас в массиве, но каждый раз в левой переменной (в данном примере entity) будет новая запись (при первом проходе это будет первая запись, при втором - вторая и т.д.).

Пример:

{% for mark in names %}
    {{ mark.name }} - имя
    {{ mark.subname }} - фамилия
    {{ mark.age }} - возраст
{% endfor %}

Где mark произвольная комбинация латинских букв и цифр, а names название метки с массивом.

Результатом данного примера будет:

Дарья - имя
Петрова - фамилия
18 – возраст
Мария - имя
Родионова - фамилия
48 – возраст
Павел  - имя
Романов  - фамилия
98 - возраст

Присваивание (Оператор set)

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

<!-- Объявляем переменную var -->
{% set var = 1 %}
<!-- Выводим переменную var -->
{{ var }}
<!-- Переопределяем переменную var -->
{% set var = var * 2 + 1 %}
<!-- Выводим переменную var -->
{{ var }}

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

Функции

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

Например:

Выводим фразу, по ключу (в д.с. 'Up') в выбранном языке, если она есть в файле локализации (data/languages/namelang.php).

{{ __('Up', module) }}

Проверяем, может ли видеть пользователь новости. Подробнее о checkAccess() читайте тут: Метки контроля прав доступа

{{ checkAccess(['news','view_list']) }}

Получаем из БД данные о пользователе и сохраняем из в переменную (fps_user_id - название метки с ID текущего пользователя)

{% set var = fetch('users', fps_user_id) %}

Кастомный вывод материалов из любого места в шаблонизаторе

Для получения данных модулей из БД в любом месте используют функцию fetch().

Список аргументов:

{% set data = fetch(model_name, entity_id=false, get_params={}, binded_fields=[]) %}

Таким образом, обязательным аргументом является только model_name . Заметьте это не название модуля или таблицы из БД, это название ORM модели, соответствующей таблице в базе данных. Такой способ вывода хоть и не самый производительный, но зато исключает возможность того, что пользователю выведутся данные, которые он не должен видеть в силу ограничений ACL и специфики модуля.

Необязательные аргументы:

  • entity_id - Указывает ID материала или материалов(массив из ID) которые будут возвращены функцией.
  • get_params - Ассоциативный массив(ключ-значение) из параметров для получения данных. Список параметров:
{
    "cache": true, // Время в секундах, на которое кешировать результат запроса.(true = 3600сек)
    "type": "DB_ALL", // Тип запроса: первый совпавший("DB_FIRST"), все совпавшие("DB_ALL"), количество совпавших("DB_COUNT") материалов.
    "sort": "name_field_for_sort", // При указании только строки выбирается режим DESC(по возрастанию), но можно указать и массив: ["name_field_for_sort", "ASC"]
    "limit": 25, // Сколько максимум найденных материалов следует возвращать.
    "page": 2, // При использовании параметра limit результат запроса делится на "куски" размером в limit материалов. Начиная с первого "куска" можно задавать какой именно "кусок" выводить. По умолчанию выводится первый "кусок".
    "fields": ["id","title"] // Позволяет выводить данные только из определенных столбцов таблицы, а не из всех сразу.
}
  • binded_fields - Служит для объединения данных из нескольких таблиц. Например, если указать ["attaches"] то в результат запроса на вывод материалов добавится поле attaches со списком прикрепленных к материалу файлов. Стоит заметить, возможность обьединения такого рода должна заранее поддерживаться ORM запрашиваемого модуля.

Обработка результата функции:

Функция возвращает результат выполнения запроса, либо массив вида {"error":"Bad request"}. Во всех случаях будет возвращен массив, так что на ошибку можно смело проверять наличием ключа "error".(Кроме типа DB_COUNT, тут результатом запроса будет число, а не массив.)

Пример вывода заголовков последних 10 материалов из модуля news c кешированием на один час:

{% set data = fetch("news", false, {"cache":true,"limit":10,"sort":"date"}) %}
{% for entity in data %}
    <h4>{{ entity.title }}</h4>
{% endfor %}

Подключение других файлов шаблона

Шаблонизатор позволяет вынести повторяющийся код в отдельный файл и подключать его так же, как include в php подключает другие файлы. В шаблонную функцию include необходимо передать полное название импортируемого файла. Предполагается, что этот файл распологается в той же папке, от куда и вызывается. Чтобы вызвать файл из папки уровнем выше, неодходимо перед адресом указать ../, то есть поддерживается всем привычная работа с адресами файловой системы. Пример импортирует содержимое файла scripts.html из папки(на примере шаблона) template/шаблон/html/default/:

{% include '../default/scripts.html' %}

внутри подключаемого файла будут работать все возможности шаблонизатора и метки, доступные в файле, где был установлен include

Updated