Роутинг (RouterApi)
Система роутинга (маршрутизации).
В рамках этого раздела будет показано, как устроена система роутинга, принципы работы роутинга, как создавать роуты, какие возможности настройки роутво есть. Описание программного интерфейса (API) доступного прикладным программистам при работе с RouterApi будет приведено в последнем разделе.
Всю систему роутинга можно условно разбить на следующие части:
- Репозитарий
router-плагинов - Программный интерфейс (API) для взаимодействия с
RouterApi- включает в себя классы и функции, которые могут использовать прикладные программисты
Общее описание репозитария router-плагинов
В основе RouterApi лежит реестр плагинов типа router. Данные плагины содержат роуты модуля, в котором добавлен такой плагин.
В свою очередь роут определяет связь адреса страницы и её обработчика. Кроме этого, в роутах же могут быть определены: Заголовок страницы, обработчик проверки прав доступа пользователя к странице.
Система реализована на основе PluginApi и работает в соответствии с особенностями PluginApi, реализуя плагины типа router.
router-плагины могут располагаться только в модулях.
На уровне файловой системы каждый router-плагин представляет собой папку вида:
{module}/src/Plugins/router/{plugin-name}
router-плагины также могут быть сгруппированы и реализованы в рамках плагинного класса.
Описание файлов 'router'-плагина
В папке плагина могут быть следующие файлы:
{plugin-name}.inc- файл-контроллерrouter-плагина, в котором добавляются роуты{plugin-name}.helpers.inc- в этом файле можно определить какие-то дополнительные функции, которые будут доступны в .inc файле
Переопределение ресурсов плагина
router-плагины могут быть переопределены.
Это достигается с учётом принятой в PluginApi последовательности поиска и добавления плагинов в реестр. Версия плагина, добавленная в реестр позднее, получает больший приоритет перед другими версиями данного плагина и, таким образом, переопределяет его полностью или отдельные его части.
Последовательность поиска и включения виджетов (плагинов типа 'router') в реестр
- core-модули
- x- и contrib-модули
- app-модули
На каждом следующем уровне могут быть переопределены/дополнены ресурсы предыдущего.
Важные принципы при переопределении router-плагина
- Файл
{plugin-name}.incпо умолчанию не переопределяет родительские версии, а расширяет (дополняет) - Файл
{plugin-name}.helpers.incпо умолчанию не переопределяет родительские версии, а расширяет (дополняет) - Если требуется переопределить какой-либо файл, то в начале имени файла нужно добавить '-'
- Если требуется переопределить плагин циликом, то перед названием его папки нужно добавить '-'. В этом случае все ранее добавленные в реестр
router-плагинов файлы данного плагина не будут учитываться. - Если в
router-плагине есть какие-либо переопределния или модификации, то перед названием его папки нужно добавить '~'. Это ни на что не повлияет, но даст понимание, что в данной папке не полноценный новый плагин, а переопределение частей уже существующего плагина.
Файл {plugin-name}.inc
Файл содержит определение роутов, которые поставляет модуль.
Определение роутов добавляются в массив $routes в соответствии с форматом, описанным ниже.
.inc-файл router-плагина подключается и обрабатывается только в момент формирования реестра роутов сайта. После этого все параметры и настройки, добавленные в $routes, сохраняются в реестр, кэшируются и в дальнейшем загружаются из закэшированного реестра.
Пример определения роута:
$routes['feedback'] = [
'title' => 'Обратная связь',
'action' => [XfeedbackMainController::class, 'feedbackForm'], //Обработчик данного действия будет методовм feedbackFormAction() в классе MainController
'access arguments' => ['xfeedback send'], //Здесь перечислены права, которые разрешают пользователю доступ к данной странице
];
$routes['feedback/requests/%requestId'] = [
'title' => 'Просмотр заявки', // Заголовок страницы
'action' => [XfeedbackMainController::class, 'viewRequest'], //Обработчик данного действия будет методовм viewRequestAction() в классе XfeedbackMainController
'action arguments' => [1], //значение второго элемента пути адреса данной страницы будует передано как первый аргумент в обработчик viewRequestAction()
'access arguments' => ['xfeedback administer'], //Здесь перечислены права, которые разрешают пользователю доступ к данной странице
];
Формат пути роута
Роуты добавляются в ассоциативный массив $routes, где в качестве ключа является путь роута.
- Путь может содержать индексрованные плейсхолдеры вида
% - Путь может содержать именованные плейсхолдеры вида
%<name>, например,%userId - Путь может состоять из одного элемента или нескольких, разделённых
/, например,user/%uid/edit - Путь не может быть пустым
- Путь не может состоять только из плейсхолдеров, первый элемент пути не может быть плейсхолдером
- Путь должен быть уникальным на уровен всего сайта, поэтому следуют придерживаться рекомандаций по формированию пути
- Путь, начинающийся с
admin/всегда относится к админке, доступ к таким страницам в обязательном порядке требует наличия у пользователя как минимум праваaccess administer pages
Рекомандаций по формированию пути
- Путь должен быть осмысленным, по возможности коротким, учитывающим иерархию, вложенность
- Все элементы пути должны быть в нижнем регистре, слова разделяются символом
- - Чтобы избежать возможного пересечения пути роута с путями роутов, определённых в других модулях, рекомендуется по возможности в каждом модуле при определении путей использовать в качестве префика id модуля. В качестве id модуля может использоваться имя модуля или более красивая форма этого имени. Например, для модуля
xfeedbackв качестве префикса путей можно было бы использоватьxfeedbackили жеfeedback, для модуляAppUsersследует использовать либоapp-users, либоusers, для модуляxmsgpможно использоватьmsgилиmessages. - Структурно сначала в пути должен быть класс ресурсов, затем id конкретного ресурса, затем действие над ресурсом (см. ниже схему и примеры)
- В случае, если в пути есть плейсхолдеры, рекомендуется всегда использовать именованные плейсхолдеры
Схематично общую структуру пути можно определить так:
{module-prefix}/{resources-name}[/%{resource-id}[/{action}]]
Примеры путей:
feedback
feedback/requests
feedback/requests/%requestId
feedback/requests/%requestId/edit
feedback/requests/%requestId/delete
feedback/requests/%requestId/reply-and-delete
Параметры роута
Будут рассмотрены следующие важные параметры:
Основные параметры:
actionaction argumentsaccess callbackaccess argumentstitletitle callbacktitle argumentstypecheck_number_partsconstraintsweight
Устаревшие (deprecated) параметры:
access_denied forwardaccess_denied redirectwidgetwidget argumentspage callbackpage argumentsfilefile path
'action' - настройка обработчика (действия)
С помощью action указываем, какой обработчик будет вызван в контроллере.
Пример:
Рассмотрим роут feedback/requests/%requestId.
'action' => [XfeedbackMainController::class, 'viewRequest']
В данном случае в качестве обработчика запроса к этому роуту будет вызван метод XfeedbackMainController::viewRequestAction().
Если в классе контроллера XfeedbackMainController определён метод viewRequestAccess(), то он будет вызван после вызова access callback, указанного в настройках роута, но до вызова обработчика viewRequestAction(). При этом метод viewRequestAccess() имеет те же входные параметры, что и viewRequestAction(). Он должен вернуть true или false. В случае возврата false вызов обработчика viewRequestAction() не будет производиться, пользователю будет возвращён код ошибки 403 Access Denied.
Если в классе контроллера XfeedbackMainController определён метод viewRequestActionAfter(), то он будет вызван после вызова обработчика viewRequestAction(), генерации и печати контента, определённого обработчиком. Данный метод будет вызван даже в случае, если вызов обработчика viewRequestAction() был отменён (например, из-за недостатка прав). Это делает данный метод крайне полезным для таких задач, как безусловный подсчёт обращений к роуту.
'action arguments' - настройка аргументов обработчика
С помощью action arguments настраиваем, как, какие и каким образом будут передаваться аргументы в обработчик роута. Чаще всего здесь настраивается маппинг элементов пути в соответствующие аргументы обработчика. Для этого есть несколько вариантов, которые будут рассмотрены ниже. Кроме этого есть возможность указать маппинг GET query-параметров, а также передача строковых значний.
Маппинг элементов пути в аргументы обработчика
Рассмотрим роут feedback/requests/%requestId/edit и соответствующий ему реальный путь feedback/requests/123/edit.
Для передачи значние 3-го элемента пути в качестве первого аргумента обработчика, а 4-го элемента в качестве второго аргумента обработчика указываем в action arguments их индексы в пути, начиная отсчёт с 0:
'action arguments' => [2, 3]
В данном случае на вход обработчика данного роута будут переданы 2 аргумента: 123 и 'edit', т.е. фактически будет следующий вызов метода:
$response = xlib(XfeedbackMainController::class)->viewRequestAction(123, 'edit');
Поскольку в данном примере у нас именованный плейсхолдер мы можем использовать имя этого плейсхолдера в action arguments, вместо указания его индекса. Это делает роут более понятным и позволяет избежать ошибок:
'action arguments' => ['%requestId', 3]
Результат передачи аргументов в обработчик будет таким же, как в примере выше.
Маппинг query-параметров URL-запроса в аргументы обработчика
Иногда возникает необходимость передать нескоторые query-параметры запроса в качестве аргументов обработчика.
Это легко сделать с помощью следующего маппинга query:{param-name}
Рассмотрим роут feedback/requests/%requestId/edit и соответствующий ему реальный запрос feedback/requests/123/edit?sendNotify=1.
Для передачи значние 3-го элемента пути в качестве первого аргумента обработчика, а 4-го элемента в качестве второго аргумента обработчика указываем в action arguments их индексы в пути, начиная отсчёт с 0:
'action arguments' => ['%requestId', 3, 'query:sendNotify']
В данном случае на вход обработчика данного роута будут переданы 3 аргумента: 123, 'edit' и 1, т.е. фактически будет следующий вызов метода:
$response = xlib(XfeedbackMainController::class)->viewRequestAction(123, 'edit', 1);
Передача простых строковых значений в аргументы обработчика
В качестве аргументов обработчика можно передавать простые строки, для которых не будет выполняться никакого маппинга:
'action arguments' => ['%requestId', 'edit']
В данном случае второй аргумент будет всегда равен 'edit'. В реальных условиях это используется редко и со временем, возможно, будет удалено.
'check_number_parts' и 'constraints' - настройка валидации роута
Перед тем как начнётся фаза проверки прав пользователя на роут, выполняется валидация роута. Это важная фаза, поскольку она позволяет предотвратить обработку заведомо некорректных запросов и передачу неподходящих данных в обработчики роута. Если валидация не пройдена, то роут считается неподходящим для обработки запроса, а значит пользователь получит ошибку 404 Not Found.
Валидация роута проходит в 3 этапа:
- Проверка соответвествия числа элементов в пути роута и в пути текущего запроса
- Проверка ограничений на значения переменных параметров роута
- Запуск валидации на уровне контроллера, указанного в
action
Рассмотрим подробнее каждый этап.
Этапа 1: Проверка соответвествия числа элементов в пути роута и в пути текущего запроса
На данном этапе проверяется простое соответствие числа элементов в пути роута и в пути текущего запроса. Данная валидация активируются, когда значение настройки роута check_number_parts установлено в true (по умолчанию). Чтобы отключить данную валидацию нужно установить check_number_parts в false.
Пример:
Пусть путь роута 'clubs/%clubId'
`check_number_parts` установлено в `true` (или не установлено)
Чилсо элементов равно 2
В этом случае валидация следующих реальных запросов даст такие результаты:
clubs/regional -> true (т.к. число элементов этого пути также равно 2)
clubs/regional/unpublished -> false (т.к. число элементов этого пути также равно 3)
Иногда (редко) требуется разрешить передачу дополнительных элементов в путь роута. В этом случае необходимо установить check_number_parts в false.
Пример:
Пусть путь роута 'clubs/%clubId'
`check_number_parts` установлено в `false`
Чилсо элементов равно 2
В этом случае валидация следующих реальных запросов даст такие результаты:
clubs/regional -> true (т.к. число элементов этого пути также равно 2)
clubs/regional/unpublished -> true (т.к. число элементов этого пути равно 3, но в данном случае это будет допустимо)
Этапа 2: Проверка ограничений на значения переменных параметров роута
С помощью constraints можно легко добавить правила валидации динамических параметров адреса роута.
Это позволяет не допустить возможную передачу неподходящих данных в обработчики роута через адрес запроса.
Также это позволяет исключить выполнение заведомо некорректных запросов.
Пример использования:
// Пусть путь роута 'user/%uid/edit
// Пусть реальный допустимый адрес будет такой 'user/123/edit?status=published'
// Добавим ограничения для переменных параметров
'constraints' => [
[1, 'TYPE', 'int'],
['%uid', 'TYPE', 'int'], //можно и так
['query:status', 'IN', ['blocked', 'published']],
],
Подддерживаются следующие типы проверок:
[paramIndex, $regexpPattern], // пример: [1, '/\d/']
[paramIndex, 'ENTITY_EXISTS', $entityTypeName], // пример: [1, 'ENTITY_EXISTS', 'User']
[paramIndex, 'OBJECT_EXISTS', $entityTypeName], // пример: [1, 'OBJECT_EXISTS', 'user_load']
[paramIndex, 'CHECK', 'is_int'], // пример: [1, 'CHECK_TRUE', 'is_int']
[paramIndex, 'TYPE', 'int'], // пример: [1, 'TYPE', 'int']
[paramIndex, 'IN', [...]], // пример: ['query:status', 'IN', ['publish','unpublish']]
[paramIndex, 'BETWEEN', [...]], // пример: ['%userId', 'BETWEEN', [1, 12]]
Этапа 3: Запуск валидации на уровне контроллера, указанного в 'action'
Если проверка constraints для текущего запроса успешно пройдена, то RouterApi проверит, есть ли у контроллера специальный метод __validateAction(). Если такой метод есть, то он будет также вызван для дополнительной валидации роута. Если этот метод также вернёт true, то валидация роута считается пройденной и далее начнётся следующая фаза обработки роута - проверка прав доступа пользователя к этому роуту.
Если же роут на каком-либо этапе не прошёл валидацию, то RouterApi вернёт ошибку route_not_found и установит access для такого роуту в false. Это приведёт к том, что пользователь увидит ошибку 404 Page Not Found, а в лог будет добавлена запись о том, что проверка constraints не прошла для текущего запроса.
'access callback' и 'access arguments' - настройка доступа к роуту
Для настройки доступа к роуту можно использовать access callback и access arguments. Рекомендуется всегда настраивать параметры доступа к каждому роуту. Если access callback не указан, то в зависимости от настройки access arguments будет использован один из следующих методов:
User::checkPerm()User::checkOwnerPerm()User::checkPerms()
'access callback'
Если этих возможностей недостаточно, то можно указать свой access callback. В качестве access callback можно передать либо название функции, либо callable-массив.
Пример:
//Файл user_common.inc
$routes['user/%user'] = [
'title' => 'My account',
'title callback' => 'user_page_title',
'title arguments' => [1],
'page callback' => 'user_view',
'page arguments' => [1],
'access callback' => 'user_view_access',
'access arguments' => [1],
];
//Файл user.module
function user_view_access($account)
{
return xlib(UserApi::class)->view_access($account);
}
'access arguments'
С помощью access arguments настраиваем, как, какие и каким образом будут передаваться аргументы в access callback.
Чаще всего здесь будут передаваться аргументы для User::check*-функций, а это один или несколько прав доступа, и иногда маппинг uid из пути роута. Правила маппинга такие же, как описано в разделе про action arguments.
Далее в этом разделе рассмотрим случаи, когда в настройках роута не указан access callback. В этом случае access callback будет выбран автоматически в RouterApi на основе содержимого access arguments.
Вариант 1 - выбор User::checkPerm()
Если в access arguments первый и единственный элемент массива простая строка, то в качестве access callback будет выбран User::checkPerm().
Пример:
'access arguments' => ['access content'],
В этом случае при проверке доступа пользователя к роуту RouterApi фактически выполнит следующий код:
$access = User::checkPerm('access content')
Вариант 2 - выбор User::checkOwnerPerm()
Если в access arguments первый элемент массива простая строка и есть второй простой (не объект и не массив) элемент массива, то в качестве access callback будет выбран User::checkOwnerPerm(). При этом второй элемент массива будет рассматриваться как параметр $ownerUid метода User::checkOwnerPerm().
Пример:
// Пусть путь роута 'user/%uid/edit'
'access arguments' => ['edit own profile', 1],
В этом случае при проверке доступа пользователя к роуту RouterApi фактически выполнит следующий код:
// Пусть реальный путь запроса будет 'user/123/edit'
$access = User::checkOwnerPerm('edit own profile', 123);
Вариант 3 - выбор User::checkPerms()
Если в access arguments первый элемент массива является массиовом, то в качестве access callback будет выбран User::checkPerms(). При этом, если также передан вотрой элемент, то он будет рассматриваться как параметр $ownerUid метода User::checkPerms().
Пример:
// Пусть путь роута 'user/%uid/edit'
'access arguments' => [
[
'edit own profile'
'edit any profile'
],
1
],
В этом случае при проверке доступа пользователя к роуту RouterApi фактически выполнит следующий код:
// Пусть реальный путь запроса будет 'user/123/edit'
$currentUid = $currentUser->uid;
$access = User::checkPerms(['edit own profile', 'edit any profile'], $currentUid, 123);
'title' - заголовок страницы
С помощью title указываем заголовок страницы.
В качестве значения можно передать обычную строку, а также строку, содержащую плейсхолдеры.
Использование плейсхолдеров позволяет строить заголовок страниц динамиески с учётом параметров адреса страницы.
В качестве плейсхолдеров можно использовать:
{%<index>}- Прямой индексированный плейсхолдер параметра из пути.{%<name>}- Прямой именованный плейсхолдер параметра из пути.{@<name>}- Вычисляемый плейсхолдер (такой плейсхолдер передаётся в специальном методе контроллера__mapTitleArgs()или путём вызоваPage::setPageTitleArg()).{query:<param-name>}- Значение (заэскейпченное) из query-параметра.
ВАЖНО: Значение плейсхолдера всегда безопасно, поскольку проходит через
escapeHtml().
Примеры:
//Простой заголовок в виде строки
'title' => 'Редактор заявки'
//Заголовок в виде строки, включающий прямой индексированный плейсхолдер параметра из пути
'title' => 'Редактор заявки #{%2}'
//Заголовок в виде строки, включающий прямой именованный плейсхолдер параметра из пути
'title' => 'Редактор заявки #{%requestId}'
//Заголовок в виде строки, включающий вычисляемый плейсхолдер
'title' => 'Редактор заявки #{%requestId} от {@requesterName}'
//Заголовок в виде строки, включающий вычисляемый плейсхолдер и параметр из query
'title' => 'Редактор заявки #{%requestId} от {@requesterName} ({query:})'
'title callback' и 'title arguments' - установка нестандартного обработчика заголовка
Если по каким-то причинам возможностей установки заголовка страницы с помощью свойства title недостаточно, то можно установить свой обработчик заголовка с помощью свойств роута title callback и title arguments.
В качестве title callback можно передать либо название функции, либо callable-массив.
Это не рекомендуемый способ генерации заголовка страницы и в будущих версиях скорее всего его поддержка будет удалена.
Пример использования:
//Файл user_common.inc
$routes['user/%user'] = [
'title' => 'My account',
'title callback' => 'user_page_title',
'title arguments' => [1],
'page callback' => 'user_view',
'page arguments' => [1],
'access callback' => 'user_view_access',
'access arguments' => [1],
];
//Файл user.module
function user_page_title($account)
{
if ($account->uid == $GLOBALS['user']->uid) {
return t('My account');
}
return $account->name;
}
'widget' и 'widget arguments' - настройка виджета как обработчика роута
В качестве обработчика роута можно использовать виджет. В этом случае вместо action и action arguments используем соответственно widget и widget arguments. Такой подход позволяет немного упростить код, избавившись от необходимости в создании контроллера и метода-обработчка, но экономия эта несущественная, а недостатков такой подход имеет довольно много.
Использование этих параметров не рекомендуется, в следующих версиях RouterApi эта возможность вероятно будет удалена.
Пример:
$routes["node/%/photo/%/info"] = [
'title' => 'File quick info',
'widget' => 'xalbum-quick-card',
'page arguments' => [1, 3],
'access arguments' => ['access content'],
];
type и weight - настройка виджета как обработчика роута
Параметр type наряду с title и weight влияет на отображение данного роута в таких элементах навигации по странице как: заголовок, табы и хлебные крошки. С параметром title заголовком страницы разобрались выше. В этом разделе рассмотрим как настроить отображение навигации по подразделам в виде табов.
Для начала рассмотрим возможные значния параметра type:
MENU_CALLBACK- значениеtypeпо умолчанию. Такой роут не является подразделом (табом).MENU_DEFAULT_LOCAL_TASK- Такой роут будет подразделом (табом) по умолчанию.MENU_LOCAL_TASK- Такой роут будет подразделом (табом)
Параметр weight имеет смысл для роутов, являющихся подразделами (табами). В этом случае weight позволяет изменить порядок отображения табов по умолчанию.
Рассмотрим пример настройки роутов для подразделов:
$routes['admin/xfeedback'] = [
'title' => 'Обратная связь',
//Обработчик данного действия будет методовм feedbackSummaryAction() в классе XfeedbackAdminController
'action' => [XfeedbackAdminController::class, 'feedbackSummary'],
'access arguments' => ['xfeedback edit'],
];
$routes['admin/xfeedback/summary'] = [
'title' => 'Сводка',
'access arguments' => ['xfeedback edit'],
//У данного типа нет обработчика.
//Он используется только для отрисовки дефолтного таба.
//Обработчик такого таба задаётся в родительском табе - в данном случае это будет 'admin/xfeedback'
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -1,
];
$routes['admin/xfeedback/list'] = [
'title' => 'Список заявок',
//Обработчик данного действия будет методовм feedbackListAction() в классе XfeedbackAdminController
'action' => [XfeedbackAdminController::class, 'feedbackList'],
'access arguments' => ['xfeedback edit'],
'type' => MENU_LOCAL_TASK,
];
Каждый подраздел (таб) может также иметь дочерние подразделы (табы). Всего разрешается иметь не более 2-х уровней табов.
Рассмотрим пример определения 2-уровневых табов:
$routes['admin/xpay'] = [
'title' => 'Платные услуги и финансы',
'action' => [AdminController::class, 'summary'],
'access arguments' => ['administer payments'],
'file' => 'xpay.admin.inc',
'weight' => -10,
];
$routes['admin/xpay/summary'] = [
'title' => 'Сводка',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
'access arguments' => ['administer payments'],
];
$routes['admin/xpay/transactions'] = [
'title' => 'Все транзакции',
'action' => [AdminController::class, 'transactionsList'],
'access arguments' => ['administer payments'],
'type' => MENU_LOCAL_TASK,
];
$routes['admin/xpay/balance-increase'] = [
'title' => 'Пополнение балансов',
'action' => [AdminController::class, 'refillList'],
'access arguments' => ['administer payments'],
'weight' => -9,
'type' => MENU_LOCAL_TASK,
];
//второй уровень вложенности табов
$routes['admin/xpay/balance-increase/balance'] = [
'title' => 'Все методы пополнения',
'type' => MENU_DEFAULT_LOCAL_TASK,
'access arguments' => ['administer payments'],
];
//второй уровень вложенности табов
$routes['admin/xpay/balance-increase/balance/robokassa'] = [
'title' => 'Robokassa',
'action' => [AdminController::class, 'robokassaRefillList'],
'access arguments' => ['administer payments'],
'type' => MENU_LOCAL_TASK,
];
RouterApi
В рамках данного раздела рассмотрим наиболее важные программные средства, которые доступны прикладным программистам при работе с RouterApi.
- API-функции системы роутинга
- API-методы класса
RouterApiи его фасадаRouter - API-методы класса
RouterPluginRegistry - API-методы класса
Url - Базовый класс для создания контроллеров
ActionControllerBase
API-функции системы роутинга
К таким функциям относятся те, которые лежат в основе роутинга:
- Функция формирования URL
url(...) - Функция формирования ссылок
l(...)
Функция url(...)
Функция url() формирует внутренний или внешний URL-адрес на основе переданных в неё параметров.
Есть 2 варианта использования данной функции:
1) Без проверки прав доступа пользователя к соответвующему запросу 2) С проверкой прав доступа пользователя к соответвующему запросу
Вариант 1 - без проверки прав доступа
В первом случае используем простой формат передачи параметра $path в функцию - в виде строки.
Пример:
$url = url("user/$uid/edit");
В данном случае url будет сформирован и возвращён в виде строки независимо от прав доступа пользователя к этой странице.
Вариант 2 - с проверкой прав доступа
Второй вариант подразумевает перед тем как начать формирования URL сначала проверить права доступа пользователя к этой странице. И если прав доступа у пользователя недостаточно, то прервать формирование URL и вернуть null.
Такой подход бывает крайне удобен, когда требуется избежать отображения ссылок/кнопок/действий, на которые у пользователя недостаточно прав.
Чтобы использовать данный вариант вместо в качестве первого параметра $path функции url() передаем индексированный массив, где первым элементом является path роута с плейсхолдерами, а последующими - значения всех плейсхолдеров. Преобразование такого массива в реальный $path будет прозведён с помощью RouterApi.
Пример:
$url = url(["user/%uid/edit", $uid]); //можно использовать именованный параметр
$url = url(["user/%uid/edit", ['%uid' => $uid]]); //можно использовать именованный параметр
$url = url(["user/%/edit", $uid]); //можно использовать индексированный параметр
В данном случае будет сгенерирован реальный url к странице, но проверка прав доступа производится не будет. Чтобы была проведена проверка прав доступа необходимо в начале пути роута добавить знак !.
Пример:
$url = url(["!user/%uid/edit", $uid]); //можно использовать именованный параметр
$url = url(["!user/%uid/edit", ['%uid' => $uid]]); //можно использовать именованный параметр
$url = url(["!user/%/edit", $uid]); //можно использовать индексированный параметр
if($url) {
$btn = "<a href='$url'>редактировать профиль</a>";
}
Функция l(...)
Функция l() на основе переданных в неё параметров формирует html-код ссылки.
Второй параметр функции $path - это путь адреса ссылки. В отношении него действуют те же правила, что и для $path в функции url().
Примеры:
$link = l('Редактировать', "user/$uid/edit"); //без проверки прав доступа
$link = l('Редактировать', ["user/%uid/edit", $uid]); //без проверки прав доступа
$link = l('Редактировать', ["!user/%uid/edit", $uid]); //с проверкой прав доступа
if($link) {
$vars['editLink'] = $link;
}
Класс RouterApi и его фасад Router
Класс RouterApi - это 'главный контроллер' системы роутинга. Именно отсюда начинается и завершается обработка любого запроса. Кроме обработки запросов данный класс также содержит ряд публичных методов, которые могут быть полезны прикладным программистам. Для удобства работы с этими методами создан фасад Router. Поскольку для доступа к этим методам рекомендуется использовать фасад, то во всех примерах этого раздела будем использовать именно методы фасада.
Класс Router предоставляет следующие публичные методы:
- buildUrl()
- buildPath()
- checkAccess()
- getRouteData()
- getRouteItem()
- setActiveRoute()
- getRoutes()
- rebuildRoutes()
Метод buildUrl($pathPattern, $pathParams = null, $options = []): ?string
Данный метод работает аналогично функции url().
Примеры использования:
$url = Request::buildUrl(["!user/%uid", 123]); // -> '<base_url>/user/123' или null, если недостаточно прав
Метод buildPath($pathPattern, $pathParams = null): ?string
Строит реальный путь запроса на основе переданного шаблона пути (путь роута) и параметров.
Правила передачи данных для формирования реального пути запроса аналогичны функции url().
Примеры использования:
$path = Request::buildPath(["!user/%uid", 123]); // -> 'user/123' или null, если недостаточно прав
Метод checkAccess($pathPattern, $pathParams = null): bool
Проверяет есть ли у текущего пользователя права на доступ к странице с роутом $pathPattern и параметрами переменных роута $pathParams.
В отличии от фукнций url(), l(), методов Router::buildPath() и Router::buildUrl() перед путём роута не нужно ставить !.
Примеры использования:
$url = Request::checkAccess(["user/%uid", 123]); // -> true, если есть права, иначе - false
Метод getRouteData(string $pathPattern): array
Если роут, соответствующий $pathPattern найден, то будет возвращён ассоциативный массив с параметрами данного роута. Иначе будет возвращён пустой массив.
Примеры использования:
$routeData = Router::getRouteData($pathPattern);
Метод getRouteItem(?string $path = null, $routerItem = null): array
Находит роут, подходящий для пути $path, формирует все роута данные для передачи
на выполнение и возвращает результирующие данные в виде массива. Если $path пустой, то будет использован $_GET['q'].
Примеры использования:
$processedRouteData = Router::getRouteItem("user/123");
Метод getRouteItem(?string $path = null, $routerItem = null): array
Находит роут, подходящий для пути $path, формирует все роута данные для передачи
на выполнение и возвращает результирующие данные в виде массива.
Примеры использования:
$processedRouteData = Router::getRouteItem("user/123");
Метод setActiveRoute(string $path): void
Программно устанавливает активный путь в $path, что определяет, какая страница загружается.
Примеры использования:
Router::setActiveRoute("user/123");
Метод getRoutes(): array
Возвращает массив всех роутов.
Примеры использования:
$allRoutes = Router::getRoutes();
Метод rebuildRoutes(): bool
Очищает кэш роутов и запускает процедуру заполнения реестра роутов. Именно в этот момент выполняются router-плагины.
После того как реестр роутов построен он кэшируется.
Примеры использования:
Router::rebuildRoutes();