ZoomX
Альтернативный вариант использования шаблонизаторов PHP.
Введение
Главная идея данного компонента - полный отказ от стандартного шаблонизатора в пользу PHP шаблонизаторов. Никаких элементов в БД, многократного парсинга контента и логики в стиле сниппета IF. Теперь вся разработка в удобном IDE редакторе, плюс полноценная поддержка версионности, а также мощный функционал и огромное сообщество разработчиков. Для управления шаблонами добавлена библиотека FastRoute, которая позволяет гибко управлять маршрутизацией запросов. Но в отличие от других пакетов MODX с подобным функционалом (CustomRequest, modxFastRouter, VirtualPage), которые срабатывают на событие OnPageNotFound
после того как MODX не нашёл указанного ресурса, в ZoomX FastRoute запускается перед стандартным маршрутизатором MODX и имеет 3 режима работы - отключён, совместный и строгий.
Кроме того на основе FastRoute реализована поддержка REST архитектуры, что позволяет строить полноценные API приложения.
Внимание
Данный компонент требует PHP ≥ 7.1.
Использование
Установите компонент используя стандартный установщик пакетов. Включите системную настройку friendly_urls
. Следующим шагом нужно настроить роуты и Smarty шаблоны. Из коробки идут 3 шаблона - "index.tpl", "base.tpl" и "error.tpl". Изначально шаблоны находятся в папке core/components/zoomx/templates/default/
. Но её можно переопределить. Даже нужно. Ведь эти шаблоны будут перезаписываться при каждом обновлении компонента.
За работу с шаблонами отвечают 2 системные настройки - zoomx_template_dir
(по-умолчанию, core/components/zoomx/templates/
) и zoomx_theme
(по-умолчанию, default
). Я советую перенести шаблоны в папку core/templates/default/
.
Темы используются для удобного разделения представлений. Например, шаблоны для десктопной версии сайта можно разместить в теме "default", а мобильную версию, соответственно, в папке "mobile". Если темы совсем не нужны, то настройку zoomx_theme
можно оставить пустой.
Теперь можно создавать свои собственные шаблоны. Вы можете сделать это для всех ресурсов или только для части. Используйте шаблоны, идущие с компонентом, для образца. Далее эти шаблоны нужно связать с соответствующими ресурсами. Делается это через роуты.
Маршрутизатор
Маршруты (роуты) настраиваются в файле core/config/routes.php
. Чтобы подключить шаблон, нужно вернуть объект класса ZoomView
. Альтернативный вариант - использовать хелпер viewx()
.
# 1. Класс
$router->get('/hello.html', function() {
// Нужно вернуть объект класса ZoomView. Первый параметр - название шаблона, второй - данные для шаблона.
return new ZoomView('hello.tpl', ['name' => 'John']);
});
# 2. Хелпер
$router->get('/users/{id}', function($id) use($modx) {
$user = $modx->getObject('modUser', ['id' => (int)$id]);
// Можно использовать хелпер.
return viewx('profile.tpl', $user->toArray());
});
Подробнее про роуты можно узнать в разделе Маршрутизация.
Можно вернуть просто строку:
$router->get('/hello.html', function() {
return '<h1>Hello, John!</h1>';
});
Информация
Предыдущий пример может вернуть ошибку 404. Избежать этого можно если:
- Существует ресурс с указанным URI.
- В системной настройке
zoomx_autoload_resource
отключена автозагрузка ресурсов для всех роутов.
Автозагрузку ресурса можно отключить и в отдельном роуте. Таким образом мы получим виртуальную страницу.
$router->get('/hello.html', function() {
// Отключить автозагрузку ресурса = виртуальная страница
zoomx()->autoloadResource(false);
return '<h1>Hello, John!</h1>';
});
Пример редиректа. Можно использовать как стандартный метод 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']);
});
Важно понимать!
Так как маршрутизатор срабатывают в самом начале запроса, то в роутах ресурс ещё не определён ($modx->resource = null
). Менеджер запроса подгрузит его позже. Но если вам очень нужен ресурс именно в роуте, то придётся самостоятельно его запросить. В этом случае автоматический поиск задействован не будет.
$router->get('article100.html', function() use($modx) {
zoomx()->getResource('article100'); // Без расширения html
return viewx('resource.tpl');
});
Режимы маршрутизации
Маршрутизатор может работать в 3-х режимах:
0
– отключен. Все указанные роуты игнорируются.1
– смешанный (мягкий). Работает и маршрутизация ZoomX и маршрутизация MODX. Если для указанного URI роут не найден, то MODX продолжит обработку запроса в обычно- режиме. Т.е. вы можете работать в режиме PHP шаблонизатора с отдельными ресурсами.2
– строгий (монопольный). Работает маршрутизация ZoomX. Если для указанного URI роут не найден, то обработка запроса будет завершена с ошибкой 404. Шаблонизатор- MODX не запускается.
Режимы указываются в системной настройке zoomx_routing_mode
.
Если с первым и последним режимами всё понятно, то в работе смешанного режима есть свои тонкости. Например, может быть такая ситуация, когда роут для указанного URI найден, а ресурса с таким URI нет. Это может случиться для роутов с масками.
$router->get('/blog/{uri:.+}', ['ArticleController', 'show']);
Для запроса site.ru/blog/abra-cadabra-fake
указанный выше роут подойдёт, но ресурса с таким URI найдено не будет. Если бы роут не был найден, то ZoomX передал бы дальнейшую обработку запроса MODX и последний сам бы занимался поиском ресурса. Но так как роут сработал, то ZoomX будет работать как в строгом режиме. Т.е. выведет свою страницу ошибки. Таким образом, ключевым моментом является наличие роута для URI запроса.
Контроллеры
Всю логику обработки роутов лучше переносить в контроллеры. Это стандартная практика в мире фреймворков. Например, ресурс site.ru/users
используется для работы с пользователями:
// Список пользователей
$router->get('/users', ['Zoomx\Controllers\UserController', 'index']);
// Данные конкретного пользователя
$router->get('/users/{id:\d+}', ['Zoomx\Controllers\UserController', 'show']);
// Добавить пользователя
$router->post('/users', [Zoomx\Controllers\UserController::class, 'create']);
Метод index
можно опустить - достаточно указать только класс контроллера
$router->get('/users', Zoomx\Controllers\UserController::class);
К сожалению формы умеют работать только с методами GET и POST. Для того, чтобы использовать другие HTTP методы, необходимо включить системную настройку "zoomx_http_method_override" (по-умолчанию, включена) и добавить в форму скрытый инпут с именем "_method" и названием метода.
<form>
<input type="hidden" name="_method" value="PUT">
...
</form>
После этого будет работать следующий роут:
// Обновление пользователя
$router->put('/users/{id:\d+}', ['Zoomx\Controllers\UserController', 'update']); // в методе update нужно указать параметр $id
Осторожно
Для некоторых роутов придётся самостоятельно определять ресурс $modx->resource
.
В большинстве случаев в обычном режиме с методами PUT, PATCH и DELETE работают редко. В основном они используются в API режиме.
Контроллеры располагаются в папке core/components/zoomx/src/Controllers/
. Пользовательский контроллер можно разместить в ней. Этот контроллер должен расширять базовый контроллер ZoomX\Controllers\Controller
.
<?php
namespace Zoomx\Controllers;
class UserController extends Controller
{
public function index()
{
return viewx('users.tpl');
}
public function show($id)
{
// Самостоятельно находим ресурс, предназначенный для вывода данных пользователя.
zoomx()->getResource('users');
// Находим пользователя.
$user = $this->modx->getObject('modUser', ['id' => $id]);
// Передаём объект $user в шаблон user.tpl
return viewx('user.tpl', compact('user'));
}
}
RESTful API
Кроме управления шаблонами FastRoute предлагает полноценный механизм RESTful API. В MODX уже есть встроенный функционал поддержки этого архитектурного стиля. Но он крайне ограничен и отстаёт от современных стандартов. Он требует создание отдельной точки входа и может работать только с одной сущностью.
Принцип построения REST запросов
Метод HTTP | URI | Метод контроллера |
---|---|---|
GET | /users | index |
GET | /users/create | create |
POST | /users | store |
GET | /users/{id} | show |
GET | /users/{id}/edit | edit |
PUT/PATCH | /users/{id} | update |
DELETE | /users/{id} | delete |
Данный режим включается автоматически при наличии заголовка Accept со значением application/json
. В этом режиме нет автоматического определения ресурса по URI. Запрос обрабатывается в контроллере или замыкании роутера и результат сразу возвращается обратно клиенту.
Шаблонизатор Smarty
В ZoomX не используется встроенный шаблонизатор MODX. Его возможности очень ограничены. В современной разработке используют отдельные PHP шаблонизаторы. И один такой уже используется в MODX. Это Smarty. У него мощные функциональные возможности. Он один из самых быстрых. И его не нужно ставить дополнительно - он уже есть в ядре. Тем, кто пользуется Fenom, легко освоит и Smarty, так как разработчик Fenom взял за основу именно Smarty и его синтаксис.
Для работы с этим шаблонизатором необходимо определить место для хранения его шаблонов. В ZoomX из коробки идёт 3 шаблона - "base.tpl", "error.tpl" и "index.tpl". Найти их можно в директории core/components/zoomx/templates/default/
. Но использовать их не желательно. Они больше для примера и быстрого старта. Лучше создать новую папку. Например, core/templates/
. И в неё скопировать папку default
вместе с её содержимым. Таким образом, ваши изменения в базовых шаблонах не пропадут при обновлении компонента.
После этого необходимо в системных настройках указать новый путь к файлам шаблонов. В настройке zoomx_template_dir
нужно указать первую часть - "core/components/zoomx/templates/". В настройке zoomx_theme
- последнюю папку "default". Если вы не хотите использовать темы, то последнюю настройку оставьте пустой. Но в этом случае файлы шаблонов нужно перенести в папку core/components/zoomx/templates/
.
Структура папок может быть такая
templates
|--default
| |--partials
| | |--head.tpl
| | |--header.tpl
| | |--navigation.tpl
| | |--footer.tpl
| |--chunks
| | |-- chunk.tpl
| | |-- chunk2.tpl
| |--base.tpl
| |--error.tpl
| |--index.tpl
В корневой папке находятся главные файлы шаблонов. В папке partials
содержатся подшаблоны страницы. В папке chunks
- чанки для использования в сниппетах. Тогда шаблон может выглядеть так:
<!DOCTYPE html>
<html lang="{'cultureKey'|config}">
{include "partials/head.tpl"}
<body>
<div class="container">
<!-- Хэдер страницы, содержащий блок навигации (главное меню) -->
{include "partials/header.tpl"}
{block "content"}{/block}
</div>
<!-- Футер страницы -->
{include "partials/footer.tpl"}
{block "scripts"}{/block}
</body>
</html>
Я советую разбивать шаблон на подшаблоны только в случае острой необходимости, чтобы не плодить количество файлов и не портить читаемость кода. Лучше пользоваться механизмом расширения и менять только те части шаблона, которые отличаются.
<!DOCTYPE html>
<html lang="{'cultureKey'|config}">
<head>
{block "title"}<title>{'pagetitle'|resource} - {'site_name'|config}</title>{/block}
<base href="{'site_url'|config}" />
<meta charset="{'modx_charset'|config}" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
{block "styles"}{/block}
</head>
...
В расширяющем шаблоне можно изменить блоки title
и styles
.
Дополнительная информация
- Видео с небольшой демонстрацией идеи.