• Блог
  • 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/. 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 Версия доступна для скачивания.

0   218

Комментарии ()

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.