Руководство Melbis Shop: оглавление
Модульный скрипт — основной строительный блок платформы Melbis. Каждый модуль решает одну изолированную задачу: формирует меню каталога, выводит карточку товара, обрабатывает корзину, выполняет фоновую задачу. Из таких блоков складывается весь сайт.
melbis_* не
правятМодули, идущие с платформой, — с приставкой melbis_ — в
своём магазине оставляют как есть. Они должны быть у
вас всегда свежими и под рукой: по ним сверяются, из них берут образец,
и они же обновляются вместе с движком.
Нужна своя логика — пусть даже одна изменённая строка в расчёте — заводится свой модуль под своей приставкой:
<компания>_inc_logic.php (или свой
обычный модуль — правило то же) со своим пространством имён:
namespace <КОМПАНИЯ>_INC_LOGIC;;melbis_* и правьте
сколько нужно — имена можно оставить, своими их делает строка
namespace;use.Так работают со всеми модулями поставки: и с демонстрационным магазином, и с готовыми AI-инструментами.
Причина простая: обновление движка приносит новые версии
melbis_*. Правка, сделанная в них, при обновлении
либо пропадёт, либо остановит обновление, а разошедшийся с поставкой
файл нельзя сравнить с эталоном — и вы теряете единственный способ
понять, что именно в вашем магазине сделано не так, как у всех. Свой
модуль от обновления не зависит вовсе.
Все модули отображаются в дереве файлов в разделе «Скрипты, шаблоны» среды разработки. Модульные скрипты сгруппированы по компании и группе — это делает навигацию удобной даже в больших проектах с десятками модулей.
При открытии модуля в редакторе его интерфейс состоит из трёх частей:
Типовой модуль — меню каталога melbis_cataloge из
демонстрационного магазина:
<?php
namespace MELBIS_CATALOGE;
use MELBIS_INC_PAGE_MENU as PAGE_MENU;
/**
* Function Main
**/
function Main($mVars)
{
// Создать указатель шаблонизатора
$tpl = MELBIS()->TplCreate();
// Найти корень каталога
$command = "SELECT id, tlevel
FROM {DBNICK}_topic
WHERE skey = 'SHOP'
";
$root = MELBIS()->SqlSelectFlat(__LINE__, $command);
// Получить разделы меню функцией библиотеки
$menu = PAGE_MENU\Menu($root['id'], $root['tlevel']);
// Передать данные в шаблонизатор
MELBIS()->TplAssign($tpl, 'MENU', $menu);
// Вернуть результат
return MELBIS()->TplFinal($tpl, 'main');
}Модуль начинается с объявления своего пространства
имён — это имя файла в верхнем регистре:
melbis_cataloge.php → MELBIS_CATALOGE. Строка
use под ним даёт подключённой библиотеке короткое имя — об
этом ниже, в разделе «Вызов функций библиотеки».
Главная функция называется Main — это
единственная обязательная часть модуля. Имя файла она не повторяет: оно
уже написано в строке namespace. Парсер вызывает именно её,
передавая входные параметры массивом $mVars.
Внутри модуля может быть сколько угодно вспомогательных
функций. Их зовут коротким именем, без приставок —
Price($id): имя модуля стоит в строке
namespace, поэтому Price из карточки товара не
столкнётся с Price из корзины. Как называть функции — в
разделе «Принятые обозначения».
Обращение к движку пишется как обычно: неквалифицированные имена
функций PHP ищет сначала в своём пространстве, а потом в глобальном,
поэтому MELBIS(), count() и любая функция
плоской библиотеки видны без изменений.
Модуль без строки namespace тоже работает — так написаны
модули прошлых лет. Все его функции лежат в общем пространстве PHP,
поэтому имена несут приставку: главная функция называется именем файла в
верхнем регистре, вспомогательные — тем же именем с Pascal-хвостом:
<?php
function MELBIS_STORE_CARD($mVars)
{
$price = MELBIS_STORE_CARD_Price($mVars['id']);
...
}
function MELBIS_STORE_CARD_Price($mId)
{
...
}Парсер ищет обе формы главной функции — сначала
MELBIS_STORE_CARD\Main, затем
MELBIS_STORE_CARD, — поэтому тег вызова модуля в шаблоне от
формы не зависит. Плоские модули и модули с пространством имён живут в
одном магазине и вызывают друг друга. Новый код пишется с пространством
имён, а как перевести в него старый модуль — в разделе «Что учесть при
переводе модуля».
Функции подключённой библиотеки зовут через строку use
под объявлением своего пространства. Так карточка товара
melbis_store_card пересчитывает цену в валюту магазина:
namespace MELBIS_STORE_CARD;
use MELBIS_INC_LOGIC_COMMON as LOGIC_COMMON;
...
$store['price_curr'] = LOGIC_COMMON\Price($store['price'], $store['price_curr_id']);Слово as необязательно. Без него библиотека остаётся под
своим полным именем — годится, когда короткое ей не нужно или не
объявлено:
use MELBIS_INC_PAGE_MENU;
...
$menu = MELBIS_INC_PAGE_MENU\Menu($id, $level);Строка use в модуле с пространством
обязательна. Без неё имя
MELBIS_INC_LOGIC_COMMON\Price() PHP разрешает относительно
текущего пространства — ищет
MELBIS_STORE_CARD\MELBIS_INC_LOGIC_COMMON\Price — и падает
в момент вызова. Обойтись без use можно только ведущим
слешем: \MELBIS_INC_LOGIC_COMMON\Price($sum, $curr_id). В
плоском модуле, наоборот, use не нужен: там имя со слешем и
так глобальное.
Писать строку целиком руками не придётся. Наберите use с
пробелом — среда покажет список всех библиотек магазина: полное имя
пространства, объявленное библиотекой короткое имя в скобках и её
описание. Выбранный пункт вставляется готовой строкой, вместе с
as и точкой с запятой, а библиотека при этом
отмечается галочкой в панели подключений сама — код
первичен, манифест идёт за ним. Уже стоявшую галочку это не трогает,
снимать галочки автоматически среда не будет никогда.
Имя в use — псевдоним внутри одного файла, а не
глобальное правило: библиотека, подключившая другую библиотеку,
объявляет свои use у себя, и вызывающему модулю знать о них
не нужно. Наследовать импорты не требуется — загрузка файлов идёт по
дереву подключений. Если имя в файле разошлось с тем, что объявила
библиотека, при сохранении появится предупреждение
Alias differs (см. «Модули-библиотеки»).
Импорт ничего не подключает. use — это
только имя; в память библиотека попадает галочкой в манифесте.
Переезд модуля прежней формы в пространство имён затрагивает не только объявления функций. Три вещи в файле нужно просмотреть глазами:
namespace вызов new DateTime(...) ищет
MELBIS_STORE_CARD\DateTime и не находит; писать нужно
new \DateTime(...). Особенно коварен catch:
catch (\Exception $e) без слеша превращается в перехват
несуществующего класса и просто никогда не срабатывает.MELBIS()->DefineCallback('PageLink', 'MELBIS_STORE_CARD_PageLink')
после переезда указывает в пустоту — движок сообщит об этом сразу, при
подключении модуля. Надёжнее не писать имя вовсе:
DefineCallback('PageLink') находит функцию в своём же файле
и переезд переживает сам. Если имя функции отличается от имени
модификатора, годится синтаксис PHP 8.1, где имя разрешает компилятор:
DefineCallback('PageLink', Link(...)).MELBIS_INC_LOGIC_ORDER_CALC\Run. Её правит владелец
вручную: код о ней не знает.Старые вызовы вашего модуля из чужих файлов после переезда тоже нужно поправить, но правка механическая — подчёркивание перед именем функции становится обратным слешем:
MELBIS_STORE_CARD_Price($id); // было
MELBIS_STORE_CARD\Price($id); // сталоБольшинство модулей следуют одному и тому же паттерну:
1. Создать указатель шаблонизатора:
$tpl = MELBIS()->TplCreate();2. Получить данные из базы данных методами
MELBIS()->Sql*:
// Вернуть плоский массив одной записи
$topic = MELBIS()->SqlSelectFlat(__LINE__, $command, $params);
// Вернуть массив записей
$menu = MELBIS()->SqlSelect(__LINE__, $command, $params);
// Вернуть массив с индексацией по ключу
$image = MELBIS()->SqlSelectEnumFlat(__LINE__, $command, 'id', $id, $params);
// Вернуть страницу записей и общее число строк
$goods = MELBIS()->SqlSelectLimit(__LINE__, $command, $offset, $limit, $params);Постраничный вывод, запись данных, транзакции и блокировки таблиц описаны в разделе «Работа с базой данных».
3. Передать данные в шаблонизатор методами
MELBIS()->Tpl*:
// Передать одно значение
MELBIS()->TplAssign($tpl, 'TITLE', $topic['name']);
// Передать массив целиком
MELBIS()->TplAssign($tpl, $topic);
// Распарсить шаблон и поместить результат в переменную
MELBIS()->TplParse($tpl, 'CONTENT', 'page_index');4. Вернуть результат — финальный парсинг главного шаблона:
return MELBIS()->TplFinal($tpl, 'main');Полный перечень методов Sql* и Tpl* описан
в соответствующих разделах документации.
Параметры поступают в модуль через массив $mVars. Способ
формирования массива зависит от того, как был вызван модуль.
Если модуль вызван как входная точка (через
MELBIS()->Run()), параметры передаются из корневого
скрипта явно:
// В index.php:
$entry_param = [serialize($_GET), serialize($_POST)];
MELBIS()->Run('melbis_base_page', $entry_param);В модуле объявлено get: serial, post: serial — и внутри
функции:
$id = (int) ( $mVars['get']['topic_id'] ?? 0 );Если модуль вызван из шаблона, параметры передаются прямо в теге вызова — позиционно, через запятую:
{MELBIS:melbis_store_image([ID],kDefault)}
{MELBIS:melbis_store_random(8)}
{MELBIS:melbis_cataloge_sub(,,[ID])}Значения переменных подставляются в квадратных скобках —
[ID], а не {ID}. Аргументы
разбираются по запятой и по закрывающей скобке, поэтому любое «грязное»
значение — название с запятой, текст с кавычкой, сериализованный массив
— при подстановке через {ID} разорвёт список аргументов и
модуль получит мусор. Квадратные скобки прогоняют значение через
urlencode, а парсер на приёме делает парный
urldecode, так что содержимое доезжает целиком и в любом
виде. Правило действует и для переменных текущей строки цикла:
{#GOODS}
{MELBIS:melbis_store_card([VAR:LANG],[ID],[PRIOR])}
{GOODS#}Литералы (kDefault, 8) пишутся как есть —
они не переменные, кодировать нечего.
В модуле должны быть объявлены соответствующие параметры, например
id: int, key: str, и внутри функции:
$id = $mVars['id'];
$key = $mVars['key'];Типы параметров объявляются в поле над редактором в IDE:
| Тип | Описание |
|---|---|
int |
Целое число |
float |
Число с плавающей точкой |
bool |
Булево значение |
str |
Строка |
fix |
Фиксированный набор значений; первое — по умолчанию. Пример:
mode: fix=list\|grid |
serial |
Сериализованный массив PHP (для передачи
$_GET/$_POST). При пустом или некорректном
значении вернётся [] — проверять is_array в
модуле не нужно |
Из шаблона — основной и безопасный способ. Парсер
встречает тег {MELBIS:имя_модуля(...)} в HTML-шаблоне,
запускает модуль и подставляет его HTML-результат на место тега.
Доступен для любого модуля без ограничений.
Как входная точка — вызов через
MELBIS()->Run() в корневом скрипте. URL при этом может
быть любым — главный критерий именно вызов через Run. Чтобы
разрешить такой вызов, в правой панели IDE необходимо включить опцию
«Модуль входной точки». Без этого флага парсер
откажется запускать модуль напрямую. Входными точками, как правило,
являются модули-роутеры страниц, обработчики форм и cron-задачи.
Фрагмент HTML, который вернул модуль, может содержать теги вызова других модулей — и так до любой глубины: парсер обрабатывает всю цепочку сам. Разбор на примере страницы демонстрационного магазина — в разделе «Архитектура витрины».
В правой панели IDE отображаются все доступные библиотечные модули
(inc). Чтобы подключить библиотеку, достаточно поставить
галочку — парсер автоматически загрузит её перед запуском текущего
модуля, и все её функции станут доступны. Библиотека, которую притянула
другая библиотека, показана серой: она уже подключена, отмечать её
незачем.
Список таблиц модуля разработчик не ведёт — движок составляет его сам, по запросам, которые модуль выполнил. Вкладка «Таблицы» показывает готовый список, и единственное действие на ней — снять галку с таблицы, от которой кеш зависеть не должен. Как это работает и когда список пополняется — в разделе «Кеширование».
Помимо работы с шаблонами и базой данных модулю время от времени нужны служебные вызовы платформы — имя текущего модуля, вызов собственной функции, путь к загруженному файлу. Они собраны в разделе «Служебные методы».