ZoomX - альтернативная шаблонизация
Введение
Главная идея данного компонента - полный отказ от стандартного шаблонизатора в пользу 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/
. Но её можно переопределить. Даже нужно. Ведь эти шаблоны будут перезаписываться при каждом обновлении компонента.
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
.