ZoomX - альтернативная шаблонизация

Введение

Главная идея данного компонента - полный отказ от стандартного шаблонизатора в пользу PHP шаблонизаторов. Никаких элементов в БД, многократного парсинга контента и логики в стиле сниппета IF. Теперь вся разработка в удобном IDE редакторе, плюс полноценная поддержка версионности, а также мощный функционал и огромное сообщество разработчиков. Но этим дело не ограничивается. Значительной переработке подвергся класс обработки ответа. Кроме оптимизации класса modRequest в функционал была встроена библиотека FastRoute для расширения возможностей стандартного механизма роутинга. Но в отличие от других пакетов с подобным функционалом (CustomRequest, modxFastRouter, VirtualPage), которые срабатывают на событие OnPageNotFound после того как MODX не нашёл указанного ресурса, в данном случае FastRoute запускается перед стандартным роутером MODX и имеет 3 режима работы - отключён, совместный и строгий.

Внимание!

Данный компонент требует PHP >= 7.0.

Использование

Установите компонент используя стандартный установщик пакетов. Включите системную настройку friendly_urls. После этого главная страница сайта будет собрана из Smarty шаблона "index.tpl", который идёт с компонентом вместе с двумя другими - "base.tpl" и "404.tpl". Изначально шаблоны находятся в папке core/components/zoomx/templates/default/. Но её можно переопределить. За это отвечают 2 системные настройки - zoomx_template_dir (по-умолчанию, core/components/zoomx/templates/) и zoomx_theme (по-умолчанию, default). Я советую перенести шаблоны в папку core/templates/default/.

Теперь можно создавать шаблоны. Вы можете сделать это для всех ресурсов или только для части. Используйте существующие шаблоны для образца. Далее эти шаблоны нужно связать с соответствующими ресурсами. Делается это через роуты.

Роутер

Роуты настраиваются в файле core/config/routes.php. Чтобы подключить шаблон, нужно вернуть объект класса ZoomView.
$router->get('hello.html', function() {
    // Нужно вернуть объект класса ZoomView. Первый параметр - название шаблона, второй - данные для шаблона.
    return new ZoomView('hello.tpl', ['name' => 'John']);
});
$router->get('users/{id}', function($id) use($modx) {
    $user = $modx->getObject('modUser', ['id' => (int)$id]);
    // Можно использовать хелпер.
    return viewx('profile.tpl', $user->toArray());
});

Подробнее в документации FastRoute.

Можно вернуть просто строку:

$router->get('hello.html', function() {
    return '<h1>Hello, John!';
});

Пример редиректа. Можно использовать как стандартный метод modX::sendRedirect(), так и переопределить переменную $modx->resourceIdentifier, указав в ней или id нужного ресурса или URI. В последнем случае для переопределённого ресурса будет использован указанный шаблон.

$router->get('product1.html', function() use($modx) {
    $modx->sendRedirect('catalog/product2.html');
});
// Использование идентификатора ресурса
$router->get('resource.html', function() use($modx) {
    // можно указать id
    $modx->resourceIdentifier = 2;  
    // или URI ресурса
    $modx->resourceIdentifier = 'another.html';
    
    return viewx('hello.tpl', ['name' => 'John']);
});

Роутер может работать в 3-х режимах:

  • 0 - отключен. Все указанные роуты игнорируются.
  • 1 - совместный (мягкий). Если для указанного URI роут не найден, то MODX продолжит обработку запроса в обычном режиме. Т.е. вы можете работать в режиме PHP шаблонизатора с отдельными ресурсами.
  • 2 - строгий. Если для указанного URI роут не найден, то обработка запроса будет завершена с ошибкой 404. Шаблонизатор MODX не запускается.

Режимы указываются в системной настройке zoomx_routes_mode.

Шаблонизатор Smarty

Smarty мощный и один из самых быстрых PHP шаблонизаторов. Так как он входит в ядро MODX выбор пал на него. У него много предустановленных плагинов (встроенные функции, пользовательские функции, встроенные модификаторы). ZoomX также добавляет несколько своих плагинов.

Информация!

В шаблонах доступен объект $modx.

Модификаторы

chunk – вызвать чанк.

Аргументы:

  • массив параметров.
// Кэшированный чанк
{'chunkName'|chunk:['foo' => 'bar']}
// Некэшированный чанк (Smarty syntax)
{'chunkName'|chunk:['foo' => 'bar'] nocache}
config – получить системную настройку.
//[[++site_name]]
{'site_name'|config}
csstohead – зарегистрировать CSS в секции HEAD страницы.

Аргументы:

  • media.
{'assets/css/styles.css'|csstohead}
{'assets/css/styles.css'|csstohead:all}
htmltobottom – зарегистрировать html блок в конце страницы.
{'HTML content'|htmltobottom}
htmltohead – зарегистрировать HTML блок в секции HEAD страницы.
{'HTML content'|htmltohead}
jstobottom – зарегистрировать JS в конце страницы.

Аргументы:

  • plaintext - true/false.
{'assets/js/scripts.js'|jstobottom}
{'<script>let foo = "bar";</script>'|jstobottom:true}
jstohead – зарегистрировать JS секции HEAD страницы.

Аргументы:

  • plaintext - true/false.
{'assets/js/scripts.js'|jstohead}
{'<script>let foo = "bar";</script>'|jstohead:true}
ignore – не парсить указанный тег, а вывести как есть.
{'content'|resource|ignore}  // вывод: {'content'|resource}
// тоже самое используя стандартный синтаксис Smarty
{literal}
{'content'|resource}
{/literal}
lexicon – вывести значение лексикона для указанного ключа.

Аргументы:

  • массив параметров.
  • код языка.
//[[%lang]]
{'lang_key'|lexicon}
// with parameters
{'lang'|lexicon:['foo' => 'bar']}
modx – распарсить тег MODX. Может быть использован для парсинга тега ресурса или TV, в контенте которых есть теги. Используется парсер, указанный в настройке parser_class.
{'[[*pagetitle]] - [[++site_name]]'|modx}
parse – распарсить тег. Как и предыдущий модификатор может быть использован для парсинга тега ресурса или TV, в контенте которых есть теги. Но в отличие от предыдущего можно указывать какой парсер использовать.

Аргументы:

  • класс парсера. По-умолчанию, указанный в системной настройке zoomx_parser_class.
// Использовать ZoomX парсер, указанный в настройке "zoomx_parser_class".
{'content'|resource|parse}
// Использовать парсер MODX.
{'[[*pagetitle]] - [[++site_name]]'|parse:'modParser'}
ph – получить MODX плейсхолдер.
//[[+modx.user.id]]
{'modx.user.id'|ph}
print – вывести отформатированное и экранированное значение.

Аргументы:

  • форматировать - true/false. Оборачивает вывод в тег <pre>. По-умолчание, true.
  • экранировать вывод - true/false. Использует функцию htmlspecialchars(). По-умолчание, true.
{$array|print}
// вывести необработанную строку
{$array|print:false:false}
resource – получить значение указанного поля текущего ресурса.

Аргументы:

  • альтернативное поле, которое будет выведено, если начальное поле пустое.
{'pagetitle'|resource}
// значением по-умолчанию является другое поле ресурса.
{'longtitle'|resource:'pagetitle'}
// значение по-умолчанию является строкой.
{'longtitle'|resource|default:'Строка'}
// TV значение
{'tv'|resource}
snippet – запустить MODX сниппет.

Аргументы:

  • массив параметров.
// Кэшированный сниппет
{'snippetName'|snippet:['foo' => 'bar']}
// Некэшированный сниппет (Smarty syntax)
{'snippetName'|snippet:['foo' => 'bar'] nocache}
// Некэшированный сниппет в набором параметров
{'snippetName@PropSet'|snippet:['foo' => 'bar'] nocache}
tv – вывести TV текущего ресурса. Альтернатива модификатору resource для вывода TV.
{'tv_name'|tv}
url - генерировать URL для указанного ресурса.

Аргументы:

  • контекст;
  • массив агрументов URL;
  • схема;
  • массив опций.
{5|url}
{5|url:'web':['foo' => 'bar']:-1}
user – получить значение поля текущего пользователя.

Аргументы:

  • альтернативное поле пользователя, которое будет выведено, если начальное поле пустое.
{'username'|user}
{'fullname'|user:'username'}

Блоки

auth – выводит содержимое блока только для залогиненных пользователей.
{auth}
Контент для аутентифицированных пользователей.
{/auth}
guest – выводит содержимое блока только для гостей.
{guest}
Контент для неаутентифицированных пользователей.
{/guest}
modx – используется для парсинга содержимого блока с помощью MODX парсера, указанного в системной настройке parser_class.
{modx}
<a href="[[~[[*id]]]]">[[*pagetitle]]</a>
{/modx}
parse – используется для парсинга содержимого блока. По-умолчанию используется парсер, указанный в системной настройке zoomx_parser_class. А там по-умолчанию указан ZoomSmarty. Хотя можно указать и любой MODX парсер. Но для этого лучше использовать соответствующий блок modx.

Атрибуты:

  • parser - класс парсера.
{parse}
<a href="[[~[[*id]]]]">[[*pagetitle]]</a>
{/parse}

// Можно указать любой другой
{parse parser="pdoParser"}
<a href="[[~[[*id]]]]">[[*pagetitle]]</a>
{/parse}

Создание собственных плагинов

Для большей части задач можно использовать непосредственно функции PHP. Например, вместо {$string|trim} можно вызвать {trim($string)}. Если данных возможностей не хватает, то можно создать собственный модификатор или функцию. Сделать это можно двумя способами:

  1. Создать файл плагина следуя инструкции и положить его в папку плагинов Smarty (системную или пользовательскую). Он будет подключён автоматически. Если вы создали свою собственную папку плагинов, то не забудьте указать её в системной настройке smarty_custom_plugin_dir.
  2. Динамически подключить во время загрузки MODX с помощью метода registerPlugin(). Для этого необходимо создать плагин на событие OnHandleRequest и в нём подключить нужный функционал:
    <?php
    // OnHandleRequest
    
    function print_current_date($params, $smarty)
    {
      if(empty($params["format"])) {
        $format = "%b %e, %Y";
      } else {
        $format = $params["format"];
      }
      return strftime($format,time());
    }
    
    // Подключение функции
    parserx()->registerPlugin("function","date_now", "print_current_date");
    // Пробросить PHP функцию ucfirst в виде модификатора.
    $smarty->registerPlugin("modifier","ucfirst", "ucfirst");
    

    В шаблоне теперь доступна функция date_now и модификатор ucfirst.

    {date_now}
    {* Или с форматированием *}
    {date_now format="%Y/%m/%d"}
    
    {$name|ucfirst}
    

Кэширование

В Smarty заложены мощные возможности кэширования. Вы можете управлять кэшированием отдельных тегов также, как это делается в MODX с помощью знака !, но несколько иначе.

// Используя специальный аттрибут nocache
{'username'|user nocache}
// или блок 
{nocache}
{'username'|user}
{'email'|user}
{/nocache}

Для подшаблонов (чанков по модыксовски) возможностей больше. Для них можно указать даже время кэширования.

{include file="chunks/header.tpl" cache_lifetime=300}

Можно указать разные кэши одного подшаблона для разных условий

// Разные кэши для разных категорий
{include file="header.tpl" cache_id="cache_key_{$modx->resource->parent}" title="{$modx->resource->pagetitle}"}

Управлять кэшированием можно с помощью системной настройки zoomx_caching. При разработке советую её отключать. Также кэш не будет создаваться для документов, у которых снят чекбокс "кэшируемый".

Информация!

Компилированные файлы шаблонов Smarty хранятся в папке, указанной в системной настройке zoomx_smarty_compile_dir. Очистить их можно или вручную или через механизм очистки кэша в админке. Файлы кэша хранятся в папке, указанной в системной настройке zoomx_smarty_cache_dir. Эта папка очищается при сохранении в админке любого элемента, ресурса, системной настройки и т.д. Данный подход является стандартным в MODX.

Системные настройки

Ключ По-умолчанию Описание
zoomx_caching true Кэшировать файлы шаблонов.
default_tpl base.tpl Используется, когда роутер работает в строгом режиме, а для текущей страницы не найден роут и не найдена страница ошибки.
zoomx_theme default Имя папки в каталоге шаблонов. Позволяет управлять темами сайта. Добавляется в путь, указанный в настройке zoomx_template_dir. Если темы не нужны, можно указать пустое значение.
zoomx_template_dir {core_path}components/zoomx/templates/ Полный путь к файлам шаблонов. Рекомендую перенести в {core_path}templates/
zoomx_routes_mode 1 Режим роутинга. Возможные значения:
0 - роутинг выключен;
1 - смешанный режим (если роут для данного URI не найден, поиском займётся MODX);
2 - монопольный режим (если роут не нейден, то обработка завершится с ошибкой 404).
zoomx_include_modx true Разрешить объект $modx в шаблонах.
modx_parser_class ZoomSmarty Класс парсера, который используется для обработки контента. Должен имплементировать интерфейс Zoomx\ParserInterface.
zoomx_smarty_cache_dir zoomx/smarty/cache/ Путь к файлам кэша шаблонов Smarty относительно папки core/cache/.
zoomx_smarty_compile_dir zoomx/smarty/compile/ Путь к компилированным файлам шаблонов Smarty относительно папки core/cache/.
zoomx_smarty_config_dir {core_path}components/zoomx/config/ Полный путь к файлам конфигов Smarty.
smarty_custom_plugin_dir '' Полный путь к пользовательским плагинам Smarty.

Планы по развитию компонента

  1. Переработать функционал запуска чанков и сниппетов. На данный момент приходится использовать стандартные методы MODX, которые жёстко завязаны на стандартный шаблонизатор. Кроме того, необходимо отказаться от костыля в виде синхронизации элемента БД с файловой версией.
  2. Добавить в копилку используемых шаблонизаторов Blade. Базовый функционал уже работает. Осталось продумать некоторые моменты использования специфики MODX.
  • Видео с небольшой демонстрацией идеи.
  • Проект на GitHub.
Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.