Модули-библиотеки

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

В имени библиотеки после inc идёт её собственная группа — melbis_inc_web_callback.php относится к группе web. По ней библиотеки и раскладываются в дереве «Среды разработки», рядом с модулями своей части проекта (см. «Принятые обозначения»).

Чтобы подключить библиотеку к обычному модулю, достаточно поставить галочку напротив неё в правой панели IDE — парсер загрузит её автоматически перед запуском модуля, и все её функции станут доступны.

Рассмотрим три характерных примера из демонстрационного магазина.

melbis_inc_web_topic — временные таблицы

Этот модуль содержит одну функцию — 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);
    ...
}

melbis_inc_web_callback — колбэки шаблонизатора

Этот модуль регистрирует модификаторы шаблонизатора. Колбэк — это 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 содержит все функции по работе с заказами: создание, загрузку, редактирование, калькуляцию, добавление и удаление товаров, расчёт скидок, уведомления:

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 типичны и другие библиотеки:

Любую логику, которая нужна более чем в одном модуле, стоит выносить в библиотеку.