Синтаксис шаблонов

Всё, что доступно в .htm-файле: подключение частей, вывод переменных, циклы и условия. Методы, которыми модуль передаёт сюда данные, описаны в разделе «Методы шаблонизатора», преобразования значений — в разделе «Модификаторы».


1. Сборка шаблона из частей

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

1.1. Сборка со стороны PHP — TplParse

Классический способ уже описан выше, в разделе «Методы работы с шаблонами»: модуль явно решает, какой файл парсить и в какую переменную положить результат:

MELBIS()->TplParse($tpl, 'ORDER', 'order_absent');

Такой подход оправдан, когда выбор шаблона зависит от бизнес-логики, которая целиком живёт в PHP — например, модуль сам решает, парсить ли order_absent или order_exist, на основании данных, до которых у вёрстки нет доступа.

1.2. Сборка внутри шаблона — тег {^ИМЯ}

Если же выбор частей — чисто вопрос вёрстки (когда показать шапку/футер, когда — заглушку вместо блока), собирать структуру из PHP не обязательно и даже нежелательно: это смешивает View-логику с Controller-кодом. Для таких случаев подключить другой .htm-файл того же модуля можно прямо в шаблоне, тегом {^ИМЯ_ФАЙЛА}:

<div class="order-page">
    {*ORDER}
        {^ORDER_EXIST}
    {ORDER*}

    {*!ORDER}
        {^ORDER_ABSENT}
    {ORDER*}
</div>

Модуль в этом случае ограничивается только передачей данных:

MELBIS()->TplAssign($tpl, 'ORDER', $order_data);

а какую именно «ветку» вёрстки показать — решает сам шаблон main.htm.

Как это работает:

Важное ограничение: нельзя разрывать один блок цикла или условия между двумя разными подключениями — то есть нельзя вынести открывающий {#STORE} в один файл, а закрывающий {STORE#} в другой. Каждый подключаемый файл должен быть самодостаточным фрагментом разметки.

Рекомендации:


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

2. Вывод переменных и работа с данными

Шаблонизатор Melbis Shop умеет работать не только с плоскими переменными, но и с глубоко вложенными массивами. Обмен данными между разными модулями системы описан в разделе «Глобальный массив».

2.1. Базовый синтаксис

Вывод значения:

URL-кодированный вывод:

Пробел кодируется как + — по правилам application/x-www-form-urlencoded. В query-string это корректно, PHP разберёт значение обратно сам; в аргументах вызова модуля — тоже, там парсер применяет парный urldecode. А вот в сегменте пути плюс так и останется плюсом: /search/[QUERY]/ для значения две звезды даст /search/две+звезды/, и это уже не тот адрес. Для пути нужен rawurlencode — он доступен как обычный модификатор (см. «Модификаторы»):

<a href="/search/{QUERY|rawurlencode}/">

Системные теги. Тринадцать значений — {PATH}, {LANG}, {CSRF_TOKEN}, {BUILD} и другие — доступны в любом шаблоне любого модуля без всякого TplAssign. Особняком стоит служебный {NULL}. Все они, вместе с важной оговоркой про модификаторы, собраны в разделе «Системные константы».

<link rel="stylesheet" href="{PATH}/statics/bundle.base.css">
<html lang="{LANG}">

2.2. Доступ к вложенным массивам (Многомерные данные)

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

Шаблонизатор автоматически “сплющивает” массивы, создавая удобные ключи, что обеспечивает мгновенную скорость рендеринга. Синтаксис: {МАССИВ:КЛЮЧ:ПОДКЛЮЧ} или {МАССИВ:ИНДЕКС:КЛЮЧ}

Примеры применения:

2.3. Работа с простыми (плоскими) списками

Часто модули передают не сложные ассоциативные массивы, а обычные списки значений (например, массив тегов, список ID, перечень цветов: ['red', 'blue', 'green']).

Для вывода таких списков в цикле парсер автоматически создает виртуальный ключ ITEM, в который помещается текущее значение.

Как это работает: Внутри цикла {#СПИСОК} ... {СПИСОК#} вы просто используете тег {ITEM}. Поскольку теперь это полноценный элемент движка, к нему применимы все модификаторы и системные переменные.

Пример (Вывод тегов через запятую, без запятой в конце):

Передан массив: $mData['TAGS'] = ['php', 'mysql', 'js'];

В шаблоне:
{#TAGS}
    <a href="/search/?q=[ITEM]">{ITEM|html}</a>{*!IS_LAST}, {IS_LAST*}
{TAGS#}

3. Логические блоки (Циклы)

Используются для перебора массивов (например, списков товаров, свойств, файлов).

Синтаксис вызова и лимиты:

Пример:

{#STORE=4}
    <div class="item {*IS_FIRST}first-item{IS_FIRST*}">
        <b>{NUM1}. {NAME}</b> 
    </div>
{STORE#}

Служебные переменные внутри цикла:

💡 Виртуальная переменная :COUNT (Проверки вне цикла) При передаче любого индексного массива в шаблон, парсер автоматически создает “плоскую” переменную с суффиксом :COUNT, содержащую размер этого массива. Это позволяет проверять количество элементов и строить логику еще до вызова самого цикла:

{*STORE:COUNT>4}
    <div class="alert">У нас более 4 товаров! Всего: {STORE:COUNT} шт.</div>
{STORE:COUNT>4*}

4. Условия (Логика отображения)

Теги условий начинаются с * и закрываются так же.

Базовые проверки:

Операции сравнения:

Умные проверки (IN и BETWEEN):

Условия по системным тегам:

Почему нет составных условий (&&, ||)

Иногда хочется написать что-то вроде {*NAME&&STATUS} ... {NAME*} — сразу в шаблоне. Это сделано намеренно: составные условия не поддерживаются, и вот почему.

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

$goods_absent = empty($goods['PRICE']) || empty($goods['STOCK']) || $goods['STATUS'] === 'disabled';

MELBIS()->TplAssign($tpl, 'GOODS_ABSENT', $goods_absent);
{*GOODS_ABSENT}
    <div class="out-of-stock">Товара нет в наличии</div>
{GOODS_ABSENT*}

Теперь в вёрстке видно что проверяется (GOODS_ABSENT — говорящее имя), а не набор операторов, который нужно расшифровывать. Вся логика лежит в одном месте — в PHP, рядом с остальной бизнес-логикой модуля, — и её можно переиспользовать, протестировать и поменять, не трогая ни один .htm-файл.


5. Комплексные примеры

Пример 1: SEO-блок страницы товара

Используем цепочки модификаторов для безопасной генерации микроразметки и мета-тегов.

<title>{PRODUCT_NAME|html} купить за {PRICE|nums} грн</title>
<meta name="description" content="{DESCRIPTION|plain|short:160}">

<script type="application/ld+json">
{ROW|json}
</script>

Пример 2: Карточка товара со сложной логикой

Проверяем наличие, считаем процент скидки на лету и выводим заглушки.

<div class="product-card {*IS_FIRST}first-item{IS_FIRST*}">
    <img src="{IMAGE_URL|def:/images/no-photo.png}" alt="{PRODUCT_NAME|html}">
    
    <h3>{PRODUCT_NAME}</h3>
    
    {*PRICE<OLD_PRICE}
        <div class="badge-discount">
            Скидка {ROW|calc:100-(price/old_price*100)|nums:0} %
        </div>
        <span class="old-price">{OLD_PRICE|num} грн</span>
    {PRICE*}
    
    <span class="current-price">{PRICE|num} грн</span>

    {*STOCK_STATUS==instock,preorder}
        <button onclick="addToCart({ID|int})">Купить</button>
    {STOCK_STATUS*}
    {*STOCK_STATUS!=instock,preorder}
        <span class="out-of-stock">Нет в наличии</span>
    {STOCK_STATUS*}
</div>

Пример 3: Таблица заказов в личном кабинете

Использование диапазонов (Range), дат и счетчиков циклов.

<table>
    <tr>
        <th></th>
        <th>Дата</th>
        <th>Сумма</th>
        <th>Статус</th>
    </tr>
    {#ORDERS}
    <tr class="{*IS_EVEN}bg-gray{IS_EVEN*}">
        <td>{NUM1}</td>
        <td>{CREATED_AT|date:d M Y}</td>
        <td>{TOTAL|num:2,., } $</td>
        <td>
            {*STATUS_ID==1-3} <span class="badge-blue">{STATUS_NAME}</span> {STATUS_ID*}
            {*STATUS_ID==4-5} <span class="badge-green">{STATUS_NAME}</span> {STATUS_ID*}
        </td>
    </tr>
    {ORDERS#}
</table>