Модули-библиотеки — это модульные скрипты группы inc. В
отличие от обычных модулей, парсер их не вызывает и шаблонов у них нет.
Их задача — содержать общие функции, которые используются сразу
несколькими обычными модулями. Это позволяет вынести повторяющуюся
логику в одно место и не дублировать её в каждом модуле отдельно.
В имени библиотеки после inc идёт её собственная группа
— melbis_inc_web_callback.php относится к группе
web. По ней библиотеки и раскладываются в дереве «Среды
разработки», рядом с модулями своей части проекта (см. «Принятые
обозначения»).
Чтобы подключить библиотеку к обычному модулю, достаточно поставить галочку напротив неё в правой панели IDE — парсер загрузит её автоматически перед запуском модуля, и все её функции станут доступны.
Рассмотрим три характерных примера из демонстрационного магазина.
Этот модуль содержит одну функцию —
MELBIS_INC_WEB_TOPIC_sub. Её задача: построить временную
таблицу в памяти со всеми подразделами заданного раздела, рекурсивно
обходя дерево категорий.
function MELBIS_INC_WEB_TOPIC_sub($mId)
{
$command = "CREATE TEMPORARY TABLE {DBNICK}_topic_sub ENGINE=MEMORY
WITH RECURSIVE topic_sub AS (
SELECT t.tindex, t.id
FROM {DBNICK}_topic t
WHERE t.id = :ID
UNION ALL
SELECT ts.tindex, t.id
FROM topic_sub ts
JOIN {DBNICK}_topic t ON ts.id = t.tindex
)
SELECT * FROM topic_sub";
$param = [
'id' => $mId
];
MELBIS()->SqlQuery(__LINE__, $command, $param);
}Зачем это нужно? Когда модуль выводит список товаров в разделе, он
должен учитывать не только товары самого раздела, но и товары всех его
подразделов. Та же таблица понадобится модулю фильтров по
характеристикам. Вместо того чтобы писать рекурсивный CTE в каждом из
этих модулей, достаточно один раз вызвать
MELBIS_INC_WEB_TOPIC_sub из библиотеки — и временная
таблица готова к использованию в последующих запросах:
// В модуле melbis_store_topic:
function MELBIS_STORE_TOPIC($mVars)
{
$id = $mVars['id'];
// Создать временную таблицу подразделов
MELBIS_INC_WEB_TOPIC_sub($id);
// Теперь можно делать JOIN с {DBNICK}_topic_sub
$command = "SELECT s.id
FROM {DBNICK}_topic_sub t_sub
JOIN {DBNICK}_topic t
ON t_sub.id = t.id
JOIN {DBNICK}_topic_store ts
ON ts.topic_id = t.id
JOIN {DBNICK}_store s
ON ts.store_id = s.id
WHERE s.no_visible = 0
ORDER BY t.absindex, ts.pos
LIMIT 100
";
$goods = MELBIS()->SqlSelect(__LINE__, $command);
...
}Этот модуль регистрирует модификаторы шаблонизатора. Колбэк — это PHP-функция, которую можно вызывать прямо из HTML-шаблона как модификатор переменной.
Регистрация вынесена в функцию-обёртку, а сам колбэк — рядом с ней:
// В библиотеке melbis_inc_web_callback:
function MELBIS_INC_WEB_CALLBACK()
{
MELBIS()->DefineCallback('page_link', 'MELBIS_INC_WEB_CALLBACK_page_link');
}
function MELBIS_INC_WEB_CALLBACK_page_link($mVars)
{
$link = ( $mVars['kind_key'] == 'kLink' ) ? $mVars['link'] : '/?topic_id='.$mVars['id'];
return $link;
}Обёртку вызывает модуль верхнего уровня — в теле файла, до своей основной функции:
// В модуле-роутере страницы, например melbis_base_page:
MELBIS_INC_WEB_CALLBACK();
function MELBIS_BASE_PAGE($mVars)
{
// ...
}Одного такого вызова достаточно на всю страницу. Реестр колбэков
общий на весь запрос, и каждый следующий модуль получает его копию в
момент своего подключения — поэтому в шаблонах вложенных модулей
(melbis_cataloge, карточка товара и прочие) модификатор
работает сам собой, и присоединять к ним библиотеку не
нужно.
Почему в теле файла, а не внутри функции модуля: тело выполняется при подключении модуля на каждом запросе, а функция модуля при отдаче из кеша не запускается вовсе — и регистрация не состоялась бы.
После этого в любом шаблоне становится доступен модификатор
$page_link, который вычисляет правильный URL раздела в
зависимости от его типа:
{#MENU}
<a href="{ID|$page_link:KIND_KEY,LINK}">{NAME|html}</a>
{MENU#}Именно так это и сделано в демонстрационном магазине: библиотеку
объявляет только melbis_base_page, а модификатор
используется в шаблонах melbis_cataloge и
melbis_cataloge_sub, где в списке библиотек не отмечено
ничего.
Регистрация должна успеть до подключения. Модуль получает те колбэки, которые зарегистрированы к моменту, когда подключили его самого. Родительский модуль успевает всегда: его PHP отрабатывает до разбора шаблона, а вложенные модули подключаются уже во время разбора. А вот сосед, зарегистрировавший колбэк позже, на уже подключённый модуль не повлияет — регистрируйте выше по дереву, а не сбоку.
Таблицы библиотеки достаются только тому модулю, который её объявил. Если колбэк читает данные из базы, эти таблицы попадут в список зависимостей кеша только у верхнего модуля; вложенные о них не узнают и при их изменении не пересоберутся. Для колбэков, которые лишь форматируют переданные значения, это неважно, а читающую из базы библиотеку стоит присоединить и к тем модулям, где она используется.
Подробнее о модификаторах и синтаксисе колбэков — в разделе «Модификаторы».
Это наиболее значимый тип библиотечного модуля.
melbis_inc_logic содержит все функции по работе с заказами:
создание, загрузку, редактирование, калькуляцию, добавление и удаление
товаров, расчёт скидок, уведомления:
MELBIS_INC_LOGIC_order_create — создать новую версию заказа
MELBIS_INC_LOGIC_order_load — загрузить текущую версию
MELBIS_INC_LOGIC_order_edit — открыть заказ для редактирования
MELBIS_INC_LOGIC_order_calc — рассчитать итоговые суммы
MELBIS_INC_LOGIC_order_goods_add — добавить товар в заказ
MELBIS_INC_LOGIC_order_goods_remove — удалить товар из заказа
MELBIS_INC_LOGIC_order_goods_discount — рассчитать скидки
MELBIS_INC_LOGIC_notify_events — проверить системные события
Эти функции вызываются из модуля корзины на витрине сайта:
// В модуле melbis_basket:
$version = MELBIS()->SessionGetValue('order') ?? MELBIS_INC_LOGIC_order_create();
$version = MELBIS_INC_LOGIC_order_goods_add($version, $store_id);
$version = MELBIS_INC_LOGIC_order_calc(null, $version);Ключевое преимущество этого подхода — единая бизнес-логика
для сайта и настольной программы. Те же самые функции из
melbis_inc_logic вызываются программой Melbis Shop, когда
менеджер работает с заказами через Windows-клиент. Настройка вызываемых
функций выполняется в программе через меню «Проектирование →
Реестр настроек», вкладка «Базовые настройки», раздел «Вызываемые
модули». Например, в разделе «Заказы → Калькуляция» указывается
имя библиотеки и имя функции, которую программа должна вызывать для
расчёта заказа.
Таким образом, покупатель, оформляющий заказ через сайт, и менеджер, редактирующий его в программе, работают через один и тот же PHP-код — без риска расхождения логики и ошибок синхронизации.
Помимо перечисленных, в проектах на платформе Melbis типичны и другие библиотеки:
inc_auth — проверка авторизации
клиента, вход, выход, работа с сессией.inc_email — отправка
email-уведомлений.inc_telegram — отправка сообщений
через Telegram Bot API.inc_curl — HTTP-запросы к внешним
API.inc_lang — работа с языковыми
настройками в мультиязычных проектах.Любую логику, которая нужна более чем в одном модуле, стоит выносить в библиотеку.