ZoomX 2.0. Контроллеры, RESTful API
Друзья, представляю на ваш суд новую мажорную версию компонента ZoomX. Она значительно доработана. В ней появился режим RESTful API, добавлена поддержка контроллеров для полноценной работы в режиме роутинга, добавлены исключения (exceptions) для более удобного управления поведением запроса и много чего другого. Вот список всех доработок:
- Появилась возможность полноценной работы в формате RESTful API.
- Добавлена поддержка контроллеров в роутерах.
- Добавлено событие OnRequestError для ошибок, коды которых отличаются от 401, 403 и 404.
- Добавлены короткие модификаторы «js», «css» and «html», которые являются псевдонимами модификаторов «jstobottom», «csstohead» and «htmltobottom» соответственно.
- Добавлена поддержка MODX синтаксиса тегов ({'*pagetitle'}, {'%lexicon'}, {'++setting'}, {'~5'}).
- Переработаны модификаторы «url» и «lexicon».
- Переименована системная настройка «zoomx_routs_mode» в «zoomx_routing_mode».
Рассмотрим все изменения по порядку.
RESTful API
Теперь в ZoomX есть полноценная поддержка архитектуры REST. Причем запросы не нужно отправлять на отдельную точку входа, как это сейчас делают многие. Достаточно отправить обычный запрос на сервер. Главное в этом запросе не забыть указать заголовок Accept
со значением application/json
. Можно, конечно, и не указывать, результат будет такой же. Но тогда будет работать стандартный обработчик запроса и выполнять лишние для API запроса действия. В связи с чем возможны ошибки дополнений, которые требуют наличия ресурса. Так что лучше сразу указать MODX, что это API запрос.
Для информации!
Режим API отличается от обычного режима тем, что в нём нет поиска документа по URI. Т.е. пришёл запрос, вы его обработали в контроллере или замыкании и вернули результат. А в обычном режиме идёт поиск документа по URI, и если он не найден, то возвращается ошибка. Причём, хочу обратить ваше внимание, поиск документа (ресурса) идёт автоматически, а не как во фреймворках, где в контроллере роута нужно самостоятельно делать запрос в базу данных. Эта логика используется в ZoomX только для всех остальных объектов системы.
$router->get('users/{id}', function(int $id) use($modx) { if (!$user = $modx->getObject('modUser', ['id' => $id])) { abortx('404', 'Указанный пользователь не найден.'); } return viewx('profile.tpl', $user->toArray()); });
В API режиме также придётся запрашивать документ самостоятельно. Но при желании вы можете запросить документ в роуте и в обычном режиме. В этом случае ZoomX не будет его искать.
$router->get('someUri', function() use($modx) { $modx->request->getResource('', 'anotheUri'); // Можно указать как URI, так и ID документа. return viewx('view.tpl'); });
API режим подразумевает ответ в JSON формате. Поэтому обработчик роута (замыкание или контроллер) должен вернуть массив, который автоматически закодируется в JSON.
$router->get('api/foo', function() { return ['foo' => 'bar']); }); // Ответ { 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" } }
Формат ответа соответствует спецификации JSON:API.
Если нужно указать дополнительные заголовки, то используйте хелпер jsonx()
. Первым параметром передаётся массив с данными, вторым — массив заголовков.
$router->get('api', function() { return jsonx(['foo' => 'bar'], ['CustomHeader' => 'Value']); });
Чтобы вернуть ошибку, нужно вызвать специальный хелпер abortx()
с кодом ошибки.
$router->get('profile', function() use($modx) { if (!$modx->user->isAuthenticated()) { abortx(401, 'Вы должны залогиниться!'); } return jsonx($modx->user->Profile->toArray()); });
Этот хелпер прерывает работу скрипта и выводит ответ с соответствующим HTTP заголовком. В данном примере 401 Unauthorized
. Для этого используются исключения. Из коробки идёт 8 исключений — для ошибок 400, 401, 403, 404, 405, 406, 415 и 503. Их можно расширять для адаптации к задаче и добавлять свои.
Внимание!
В отличие от примеров в официальной документации FastRoute URI в роутах не должны начинаться со слэша. Он будет удалён. Сделано это для того, чтобы URI запроса соответствовал URI документа. Ведь вряд-ли кто-то указывает в документе URI со слешем в начале. Но в самом запросе на фронте вы сами определяете, указывать слэш или нет (для относительной ссылки).
Контроллеры
Для того, чтобы не раздувать файл роутов, логику обработки запроса лучше перенести в контроллеры.
$router->get('users', ['Zoomx\Controllers\UserController', 'index']); $router->post('users', ['Zoomx\Controllers\UserController', 'create']); // Метод index можно опустить - достаточно указать только класс контроллера $router->get('users', Zoomx\Controllers\UserController::class);
Событие OnRequestError
Как вы знаете, в MODX есть 2 события для ошибок — OnPageNotFound и OnPageUnauthorized. А в ZoomX добавлен ряд ошибок и для них эти события не подходят. Поэтому я добавил ещё одно общее событие OnRequestError
, которое будет срабатывать как на ошибки, указанные выше (400, 405, 406, 415 и 503), так и на добавленные пользователем.
Вывод ошибок
Для вывода ошибок из коробки идёт шаблон error.tpl
. Он используется для всех указанных выше ошибок. Если вам нужно персонифицировать какую-то ошибку, то создайте шаблон с именем этой ошибки. Например, для ошибки 404 файл шаблона должен называться 404.tpl
. Тогда он автоматически подтянется для вызова abortx('404')
.
Короткие модификаторы «js», «css» and «html»
Они всего лишь являются псевдонимами модификаторов «jstobottom», «csstohead» and «htmltobottom» соответственно. По правилам разработки яваскрипт добавляется в конец страницы, стили в HEAD, а разметка HTML в BODY. Поэтому зачем писать такие длинные названия? )
MODX синтаксис тегов
Данная фича предлагает пользователям, привыкшим к стандартному синтаксису MODX, пользоваться похожим синтаксисом для более комфортной работы. На данный момент поддерживаются следующие теги — {'*pagetitle'}
, {'%lexicon'}
, {'++setting'}
, {'~5'}
. Но я бы советовал по возможности не использовать этот синтаксис, так как это создаёт дополнительную, хоть и небольшую, нагрузку на парсер.
Модификаторы «url» и «lexicon»
По опыту использования этих модификаторов в их логику внесён ряд изменений, исправляющих некоторые неудобства. Если раньше для использования лексикона вам было необходимо сначала загрузить нужный словарь, то теперь вы можете это сделать прямо в модификаторе:
// Было # 1. Сначала нужно загрузить лексикон {$modx->lexicon->load('ru:custom:default')} # 2. Теперь можно вывести сообщение {'some_message'|lexicon:['id' => 5]:'ru'} // Стало {'some_message'|lexicon:['id' => 5]:'ru:custom:default'}
В модификаторе url
изменения коснулись параметров. Изначально, как и в Fenom, в модификаторе есть несколько параметров, которые зеркалят метод modX::makeUrl(). В итоге, если вам нужно указать схему (третий параметр), то придётся указать и все предыдущие. Теперь достаточно сразу в первом параметре в массиве передать нужный аргумент.
// Было {5|url:'':[]:-1} // Стало {5|url:['scheme' => -1]}
Отмечу, что работает и старый формат. Т.е. можно пользоваться любым удобным вариантом.
Заключение
Вот такая получилась версия. Все изменения не раскроешь в короткой статье. Да это и не нужно. Постараюсь сделать это более подробно в доступной форме. Очень хотелось бы, чтобы и в RU сообществе обратили внимание на этот компонент и мы все вместе довели его до совершенства и сделали MODX более современным. Я уже установил его на этот сайт и впечатления самые положительные — вся разработка перенеслась в PhpStorm со всеми вытекающими плюшками. Но хотелось бы услышать больше отзывов и предложений.
Следующий важный шаг — замена pdoTools на свою библиотеку. Тем и займусь.
Комментарии ()
Вы должны авторизоваться, чтобы оставлять комментарии.
Столкнулся с проблемой при использовании сниппета pdoPage в шаблонах smarty
Есть такой вызов прямо в шаблоне смарти
Всё выводится нормально и результат и пагинация, но в ссылках пагинации некорректно сформированы адреса для страниц, а именно отсутствует адрес самой страницы. В ссылке есть лишь гет-параметры.
Во всех чанках этого сниппета за адрес ссылки отвечает плейсхолдер [[+href]]
Так вот когда я использую fenom в шаблонах — ссылки формируются корректно, но когда smarty — нет
В чем тут может быть загвоздка?
Я сейчас и занимаюсь вопросом замены pdoTools для ZoomX. Практически всё уже есть в modHelpers. Осталось добавить некоторые моменты типа работы с деревом ресурсов и написать примеры.
При чем я побовал обернуть всё это в блоки с парсерами MODX и Pdo и всё равно такой результат.
Хотя в этом же случае смарти вообще не лезет в эти блоки
Т.е. URI записывается в GET параметр с именем «q». Ну 10 лет назад ещё ладно. Но сейчас в этом нет необходимости. Достаточно просто перенаправить на index.php
Для работы с адресной строкой есть много Request менеджеров. Да банально просто в $_SERVER['REQUEST_URI'] глянуть.
Так вот, Василий работает именно с этим GET параметром q. И оттуда берёт URI страницы. А я в ZoomX работаю как сейчас принято и поэтому удаляю этот параметр, чтоб не мешал разработчикам. Чего делать, не знаю. Наверно, нужно его оставлять на такие случаи. Ё моё. Как много завязано на легаси.