ZoomX. Файловые плагины
Итак, друзья, вышла версия 3.4.0, которую я анонсировал ещё месяц назад, но отложил из-за работы над pdoTools3. Я продолжаю продвигать фреймворковский подход при разработке на MODX. Пока времени хватает только на кодинг и документацию. Но я очень надеюсь, что дойдут руки и до миникурса по разработке сайтов на ZoomX. В планах снять серию видео.
Что же в этой версии новенького?
- Файловые плагины.
- Модификатор
markdown
. - Механизм кэширования сниппетов.
- Короткие имена контроллеров в роутах.
- Упрощённый вариант переадресации в роутах.
- Событие «OnBeforeRouteProcess».
- Доработана функция
jsonx
. - Функционал контейнера.
Файловые плагины
Возможно не все знают, но я уже решал эту задачу в своём дополнении Middlewares. В ZoomX я предлагаю доработанную версию файловых плагинов. Давайте познакомимся с ними.
Файловые плагины в отличие от файловых сниппетов представляют собой классы. Вот пример такого плагина.
<?php // core/elements/plugins/MyPlugin.php class MyPlugin extends \Zoomx\Elements\Plugin { // События и приоритеты. public static $events = [ 'OnMODXInit' => -101, 'OnHandleRequest' => 0, ]; public $disabled = false; public function OnMODXInit($scriptProperties = []) { // ... } public function OnHandleRequest() { // ... } }
Как видите, названия методов совпадают с названиями событий. Таким образом, один класс содержит функционал для разных событий в отличие от того же Laravel, где для каждого события создаётся отдельный класс. В свойстве $events
указываются события, которые должны сработать. Это добавляет гибкости, так как можно запускать только указанные события. Также плагин можно отключить с помощью свойства $disabled
.
Для того, чтобы плагин сработал, его нужно подключить. Делается это специальном файле настроек elements.php
, который должен находится в директории конфигов. По-умолчанию, это /core/config/
, но её можно переопределить. В нём необходимо загрузить класс плагина и зарегистрировать его.
<?php // core/config/elements.php # 1. Загрузка класса include MODX_CORE_PATH . 'elements/plugins/MyPlugin.php'; # 2. Регистрация в системе $elementService->registerPlugins([MyPlugin::class]);
Это самый простой вариант. Но я предлагаю более красивый и современный способ — автозагрузка классов через композер. В этом случае для классов нужно придумать пространство имён. Пусть это будет «Site\Plugins».
<?php namespace Site\Plugins; class MyPlugin extends \Zoomx\Elements\Plugin { ... }
Теперь это пространство имён нужно добавить в автозагрузчик.
<?php // core/config/elements.php # 1. Регистрация пространства имён в автозагрузчике композера zoomx()->getLoader()->addPsr4('Site\\Plugins\\', MODX_CORE_PATH . 'elements/plugins/'); # 2. Регистрация в системе $elementService->registerPlugins([Site\Plugins\MyPlugin::class]);
Итак, как подключить свои файловые плагины, разобрались. А как подключить файловые плагины дополнений? Также просто. Нужно в дополнении создать такой же файл elements.php
и положить его в папку /core/components/{extra}/
(extra — название вашего дополнения). ZoomX автоматически его подключит и зарегистрирует указанные в нём плагины.
Дополнительно!
Файл elements.php
может быть использован не только для подключения плагинов. В нём можно инициализировать дополнительный функционал файловых элементов. Если, например, у вас сниппеты (или чанки) расположены в разных папках и вам не хочется прописывать лишние пути в названии сниппета/чанка, то можно зарегистрировать эти пути. Для примера возьмём такой вызов:
{'@FILE /folder/folder/snippet.php'|snippet:[ 'tpl' => '@FILE tpl/rows/row.tpl' ]}
Выглядит не очень. Исправляем.
<?php // core/config/elements.php # 1. Добавляем ещё один путь к файловым сниппетам $elementService->addSnippetPath('path/to/snippets/' . 'folder/folder/'); // Разделение для наглядности # 2. Добавляем ещё один путь к чанкам и шаблонам Smarty parserx()->addTemplateDir('path/to/chunks/tpl/rows/');
Теперь вы можете указать просто имена файлов.
{'@FILE snippet.php'|snippet:[ 'tpl' => '@FILE row.tpl' ]}
Код выглядит уже значительно приятнее.
Модификатор markdown
Уверен, можно не объяснять что это и для чего нужно.
... {'content'|resource|markdown} ...
Механизм кэширования сниппетов
Сейчас MODX предлагает следующую логику кэширования — или сниппет никогда не кэшируется или кэшируется с сохранением результата на странице. Т.е. или он будет вызыватся при каждом запросе, или после первого выполнения он будет заменён на результат и больше работать не будет. Но этих вариантов бывает недостаточно. Приходилось писать сниппеты-обёртки, которые сохраняли результат на определённое время. Теперь это можно сделать прямо из коробки. В теге сниппета в атрибуте cache_lifetime
нужно указать количество секунд для хранения результата в кэше.
{'name'|snippet:[...] cache_lifetime=3600 nocache}
Внимание!
Если включено кэширование шаблонов, то обязательно нужно указать атрибут nocache
в теге. На странице должен остаться вызов сниппета, а не его результат.
Короткие имена контроллеров в роутах
Если все контроллеры находятся в одном пространстве имён, то зачем постоянно их указывать. Теперь в системной настройке zoomx_controller_namespace
можно указать неймспейс контроллеров, а в роутах писать только имя класса.
$router->get('uri', ['MyController', 'method']); // Преобразуется в Zoomx\Controllers\MyController
Если нужно указать контроллер из другого пространства имён, то нужно первым символом в имени указать обратный слеш. В этом случае базовое пространство имён добавляться не будет.
$router->get('uri', ['\Another\Controllers\MyController', 'method']);
Упрощённый вариант переадресации в роутах
Если для переадресации не нужны никакие проверки, то можно воспользоваться специальным методом redirect
маршрутизатора.
$router->redirect('uri_from', 'uri_to'); // С указанием кода $router->redirect('uri_from', 'uri_to', 301);
Переадресовывать можно и роуты с масками. В URI переадресации нужно указать соответствующую переменную со знаком $
в фигурных скобках.
$router->redirect('articles/{id}', 'posts/{$id}');
Событие «OnBeforeRouteProcess»
Данное событие сработает перед вызовом хандлера роута (контроллера или фукнции). В нем можно выполнить определённые проверки и изменить стандартное поведение. В плагине будут доступны 2 переменных:
$uri
— текущий URI.$router
— объект маршрутизатора. С помощью его методаgetRouteVars()
можно получить переменные роута (маршрута), указанные в масках. МетодsetRouteVars()
сохраняет переменные роута для дальнейшего использования.
Как пример — можно по id ресурса/пользователя получить объект и сохранить его обратно в роутер. Таким образом в методе контроллера будет уже не id, а объект ресурса/пользователя.
<?php Namespace Site\Plugins; class Plugin extends \Zoomx\Elements\Plugin { public static $events = [ 'OnBeforeRouteProcess' => 0, ]; public function OnBeforeRouteProcess($properties) { // Распаковываем массив в переменные $uri и $router. extract($properties); // Получаем переменные маршрута $vars = $router->getRouteVars(); // Получаем объект пользователя по полученному id $vars['user'] = $this->modx->getObject('modUser', (int)$vars['user']); // Сохраняем новые данные $router->setRouteVars($vars); } }
В роуте вы вместо id получите объект пользователя.
$router->get('users/{user:\d+}', function($user) { // $user - объект пользователя return viewx('user.tpl'); });
Функция jsonx
Для удобства в эту функцию добавлен третий параметр для указания HTTP кода ответа.
$router->get('uri', function() { // получение данных ... return jsonx($array, $headers, 201); // Код ответа: 201 Created });
Если код будет меньше 400, то переменная success
будет равна true.
{ success: true, data: { foo: "bar" }, meta: { total_time: "0.0230 s", query_time: "0.0000 s", php_time: "0.0230 s", queries: 1, memory: "2 048 kb" } }
Для кода 400 и выше признак успешности будет false, а данные по ошибке будут в свойстве errors
:
{ success: false, data: {}, errors: { foo: "bar" // данные из функции jsonx }, meta: { ... } }
Функционал контейнера
Ещё в главный сервис была добавлена возможность сохранять данные для последующего использования. Это часть функционала DI контейнера. Это промежуточная ступень к внедрению полноценного DI контейнера если появится потребность.
Сохранить в контейнер -
zoomx()->set('foo', $object); // или так zoomx(['foo' => $object]);
Затем в любом месте приложения можно получить сохранённую переменную.
$object = zoomx('foo');
Забыл добавить фабрику, чтобы при запросе можно было получать не только сохранённые данные, но и создавать их по заранее прописанному алгоритму. Новогодняя суета виновата. Добавлю в следующей версии.
Заключение
В планах ещё много чего задумано. Но теперь всё это будет реализовано только в следующем году. Надеюсь, Новый год будет добр к нам всем и позволит реализоваться всему задуманному. Всех с Наступающим!
П.С. Я пока отключил в магазине эту версию по причине ошибки при установке. При новой установке всё Ок. Но при обновлении из-за кэша плагина предыдущей версии возникает ошибка. При проверке на этом сайте я перед установкой видимо что-то сохранил и кэш очистился. Поэтому новая версия встала без ошибок. А если кэш не удалить, то 500-я ошибка. После выходных пересоберу пакет с исправлением данного бага и выложу в магазин.
П.П.С. 27.12.2021 Версия доступна для скачивания.
Комментарии ()
Вы должны авторизоваться, чтобы оставлять комментарии.
Если мы определяем переменную для pdoTools в файловом шаблоне smarty, в таком варианте все работает:
Но если добавить флаг nocache, вылетает 500 ошибка «Argument 2 passed to Zoomx\Support\ElementService::runSnippet() must be of the type array, null given, called in ...\core\components\zoomx\smarty\plugins\modifier.snippet.php on line 25»:
В общем сломал голову уже, не понимаю, что ему не нравится.
P.S. Вызывается в шаблоне именно так для примера, иногда требуется получить динамические параметры в переменную через сниппет, на pdoTools есть проекты с такими решениями, проблем не было.
Иерархия документов:
-Home(1)
-Статьи(2)
--Статья1(3)
--Статья2(4)
--Статья3(5)
Набор параметров @articles:
parents→2
limit→3
tpl→@INLINE {$pagetitle}
А вот это совершенно удивительная конструкция
Время кэширования указывается в третьем параметре метода
С пропажей набора параметров при втором вызове разберусь. Моя недоработка.
Конструкция это конечно дурацкая, извините, что отвлек, только разбираюсь.
По плейсхолдеру ph так и не понял, не нашел в документации zoomX→шаблонизатор smarty ни $ph['tv...'] ни $_ph['tv...'], иначе бы не писал
P.S.: с меня причитается, как закончу проект (на modx.pro есть ссылка для благодарности)
Судя по всему шаблонизатор вообще не воспринимает переменные в параметрах:
Ну и не совсем ошибка, скорее наблюдение, если поставить запятую после последнего параметра, краш с 500 ошибкой, хотя ожидаешь по правилам PHP, что в конце массива допускается запятая.