Версия: 5.x
Маршруты

Общие сведения

С помощью маршрутов система узнает какому фронт-контроллеру нужно передать управление для обработки запроса.

Общий порядок работы маршрутов в системе выглядит так:

  • Скрипт index.php получает запрос
  • Система генерирует событие getroute
  • Все модули возвращают системе свои маршруты
  • Система перебирает маршруты, вызывая у каждого метод match. Метод возвращает true, если текущий URL подходит для маршрута
  • Если какой-либо маршрут вернул true, у него вызываются методы getController() и getAction()
    • Запускается фронт-контроллер
  • Если ни один маршрут не откликнулся на URL, возвращается ошибка 404

Классы для работы с маршутами описаны в пространстве имен RS::Router. Чтобы сообщить системе маршруты модуля, необходимо вернуть их объекты с помощью обработчика события getroute.

namespace ModuleName\Config;
class Handlers extends \RS\Event\HandlerAbstract
{
function init()
{
$this->bind('getroute');
}
public static function getRoute($routes)
{
$routes[] = new \RS\Router\Route('modulename-front-controller', array(
'/section1/{id}/{page}/',
'/section3/{id}/'
), null, 'Страница демонстрационного модуля');
return $routes;
}
}

Рассмотрим класс маршрута RS::Router::Route подробнее. Он принимает следующие аргументы:

  • string $id - идентификатор URI.
  • string |array $masks - Маска для URI в формате ReadyScript. В случае, если передан array, маска, имеющая бОльшую длину должна располагаться в списке выше.
  • array | null $defaults - Значения по умолчанию для переменных из URI
  • string $description - Текстовое описание страницы по данному URI
  • boolean hide - Скрывать из списков в административной панели?
  • string $wrap_pattern - Обрамляющий шаблон для регулярного выражения. Стандартный обрамляющий шаблон: ^{pattern}

Допустимые варианты спользования маски ReadyScript:

/product/{alias}/ - запишет вторую секцию в переменную alias
/product/{alias:[\d]+}/ - маршрут сработает, только если alias будет числом

Чтобы маршрут сработал, должно произойти соответствие по левой части маски (если используется стандартный $wrap_pattern).
Например, для url: http://domain.ru/product/phone-360gs/comments/

  • маршрут с маской /product/ - сработает
  • маршрут с маской /comments/ - НЕ сработает

В маске можно указать переменные, которые должны быть извлечены из URL. Если таких переменных в URL не будет найдено значения будут взяты из аргумента $default.

Опционально, в маске можно использовать регулярное выражение для проверки значения параметров. Для этого используется следующая конструкция: {ИМЯ_ПЕРЕМЕННОЙ[:PCRE_РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ]} Например, можно составить маску /product/{alias:(one|two|three)}/, в этом случае маршрут сработает только для следующих URL адресов:

  • /product/one/
  • /product/two/
  • /product/three/

Связь маршрута и фронт-контроллера задается либо заданием в $defaults пары:

array(
'controller' => 'сокращенное имя контроллера'
)

либо указанием сокращенного имени контроллера в качестве $id маршрута.

Заметки
Сокращенное имя контроллера формируется из имени класса контроллера включая namesapce, путем замены обратных слешей на минусы, и исключения секции -controller. Минусы по краям удаляются. Вся строка приводится к нижнему регистру. Например, если имя контроллера: \Catalog\Controller\Front\ProductList, то его сокращенное имя будет: catalog-front-productlist

Чтобы направить маршут на определенное действие контроллера, необходимо задать строку в массиве $default с ключем Act:

namespace ModuleName\Config;
class Handlers extends \RS\Event\HandlerAbstract
{
function init()
{
$this->bind('getroute');
}
public static function getRoute($routes)
{
// Обращение к /viewitem/35/ вызовет контроллер \ModuleName\Controller\Front\Controller::actionView()
$routes[] = new \RS\Router\Route('modulename-front-controller', array(
'/viewitem/{id}/'
), array(
'Act' => 'view'
), 'Страница просмотра объекта');
return $routes;
}
}

Переменную Act можно также указать в маске URI маршрута, в этом случае от URL будет зависеть, какое действие контроллера будет вызвано.

...
public static function getRoute($routes)
{
// Обращение к /item/view/35/ вызовет контроллер \ModuleName\Controller\Front\Controller::actionView()
$routes[] = new \RS\Router\Route('modulename-front-controller', array(
'/item/{Act}/{id}/'
), null, 'Страница просмотра объекта');
return $routes;
}
...

В случае, если необходимо создать несколько различных URL, ведущих на различные действия одного контроллера следует помнить, что имя маршрута должно быть уникальным и имя контроллера следует задавать с помощью параметра controller, через параметры $defaults.

namespace ModuleName\Config;
class Handlers extends \RS\Event\HandlerAbstract
{
function init()
{
$this->bind('getroute');
}
public static function getRoute($routes)
{
// Обращение к /items/ вызовет контроллер \ModuleName\Controller\Front\Controller::actionIndex()
$routes[] = new \RS\Router\Route('modulename-front-controller-list', array(
'/items/'
), array(
'controller' => 'modulename-front-controller'
'Act' => 'index'
), 'Страница со списком объектов');
// Обращение к /items/ вызовет контроллер \ModuleName\Controller\Front\Controller::actionView()
$routes[] = new \RS\Router\Route('modulename-front-controller-view', array(
'/items/{id}/'
), array(
'controller' => 'modulename-front-controller'
'Act' => 'view'
), 'Страница просмотра объекта');
return $routes;
}
}

Формирование URL с помощью маршрутов

Маршрут способен не только извлекать параметры из URL, но и формировать URL по заданным параметрам. Благодаря этой возможности в шаблонах можно абстрагироваться от конкретных URL, что дает возможность программистам и специалистам SEO, полностью изменять карту URL-адресов интернет-магазинов. Это также может быть полезно для сохранения адресации, при переносе другого сайта на платформу ReadyScript.

Итак, для формирования URL служит метод RS::Router::Route::buildUrl. Метод ожидает 3 параметра:

  • array $params - параметры для uri
  • bool $absolute - если true, то вернет абсолютный путь
  • mixed $mask_key - индекс маски по которой будет строиться url, если не задан, то будет определен автоматически

Пример:

$route = new \RS\Router\Route('modulename-front-controller', array(
0 => '/section1/{id}/{page}/',
1 => '/section3/{id}/'
), null, 'Страница демонстрационного модуля');
echo $route->buildUrl(array( 'id' => 35 )); //Вывод: /section3/35/
echo $route->buildUrl(array( 'id' => 35, 'page' => 2)); //Вывод: /section3/35/2/
echo $route->buildUrl(array( 'id' => 35, 'param1' => 'value1' )); //Вывод: /section3/35/?param1=value1
echo $route->buildUrl(array( 'id' => 35, 'page' => 2, 'param2' => 'value2' )); //Вывод: /section3/35/2/?param1=value1&param2=value2
//Принудительно задаем $mask_key = 1, чтобы указать индекс используемой маски
echo $route->buildUrl(array( 'id' => 35, 'page' => 2), false, 1); //Вывод: /section3/35/?page=2

Во всех marty шаблонах присутствует переменная $router, в которой находится объект RS::Router::Manager. Рассмотрим пример, как получить готовый URL в шаблоне.

1 Ссылка: {$router->getUrl('modulename-front-controller', ['id' => 35])} //Покажет: /section3/35/
2 Абсолютная ссылка: {$router->getUrl('modulename-front-controller', ['id' => 35, 'page' => 2], true)} //Покажет: http://вашсайт.ру/section3/35/2/

Получение текущего маршрута

Для получения текущего маршрута в любой части кода, можно воспользоваться статическим методом RS::Router::Manager::getCurrentRoute

$route = \RS\Router\Manager::getCurrentRoute(); //Вернет объект маршрута
echo $route->getId();

Очень удобно использовать объект текущего маршрута передачи сведений от фронт-контроллера к блок-контроллерам. Например, фронт-контроллер карточки товара, записывает объект просматриваемого товара в произвольное свойство product текущего маршрута.

class Product extends \RS\Controller\Front
{
function actionIndex()
{
//...
$this->router->getCurrentRoute()->product = $product_orm; //Прикрепляем к маршруту загруженый объект товара
//...
}
}

Далее, в любом блок контроллере можно получить объект товара, который просматривается в данный момент.

namespace ModuleName\Controller\Block;
class BlockController extends \RS\Controller\Admin\Crud
{
function actionIndex()
{
$route = $this->router->getCurrentRoute();
if ($route->getId() == 'catalog-front-product' && $route->product['title'] == 'Планшет Samsung') {
//Показать информацию в блоке, которая актуальна для показа на странице просмотра планшета Samsung
//...
}
}
}

Проверка маршрутов из административной панели

Чтобы проверить, как маршрут откликается на на URL, в административной панели есть вспомогательный раздел. В меню Управление → Настройка системы в правой части стрницы нужно выбрать пункт Маршруты в системе.

admin_routes.png
Маршруты в системе

В данном разделе можно набрать произвольный URL и увидеть какой маршрут откликается на него.

Маршрут административной части

Все запросы административной части обрабатываются одним маршрутом, который объявлен следующим образом:

new Route(self::ADMIN_ROUTE,
array(
"/{$admin}/{mod_controller}/",
"/{$admin}/",
), array(
'controller' => 'main-admin-index',
'mod_controller' => 'main-widgets'
),
t('Панель Администратора'),
true
);

Рассмотрим подробнее данный маршрут:

Все запросы, с начальной секцией /{$admin}/ обрабатываются фронт-контроллером main-admin-index, т.е. классом Main::Controller::Admin::Index. Второй секцией определена переменная $mod_controller, в которой ожидается сокращенное название контроллера модуля административной части, которому будет передано управление. Если $mod_controller явно не задана в URL, то будет использоваться значение по-умолчанию, т.е. main-widgets.

Заметки
Сокращенное имя контроллера, ожидаемое в параметре $mod_controller, формируется из имени класса контроллера включая namesapce, путем замены обратных слешей на минусы, и исключения секции -controller-admin. Минусы по краям удаляются. Вся строка приводится к нижнему регистру. Например, если имя контроллера: \Catalog\Controller\Admin\Ctrl, то его сокращенное имя будет: catalog-ctrl