В даной статье мне хотелось бы рассказать Вам о маршрутизаторах (Routing, Роутинг) в Framework Кохана 3.3.
Роутинг дает возможность преобразовать введеный пользователем адрес в информацию для выполнения запроса фреймворком. Т.е. после перехода по ссылке в браузере, происходит ее обработка, согласно определенным правилам. Также существует обратная маршрутизация, которая из набора параметров маршрута составляет URL.
Объявление
Для начала давайте рассмотрим структуру роута который записан в самом низу файла bootstrap.php по умолчанию:
Route::set('default', '(<controller>(/<action>(/<id>)))') ->defaults(array( 'controller' => 'welcome', 'action' => 'index', ));
Здесь мы видим статический метод set класса Route которому передается два параметра. Первый параметр - это имя данного роутера, вторый параметр - URI, причем обычно он содержит сегменты маршрута, описывающие отдельные части. В данном примере сегмент <controller> - это имя контроллера, <action> - имя метода в контролере, <id> - дополнительный параметр. Название сегмента нужно обязательно оборачивать в угловые скобки.
Также URI может содержать необязательные сегменты, они заключаются в круглые скобки. В нашем примере URI целиком состоит из необязательных сегментов.
Все слеши в начале и конце адреса автоматически удаляются при создании запроса, поэтому для роутинга адреса welcome, /welcome и /welcome/ одинаковые.
Стандартные параметры
В Route есть ряд параметров которые не обязательны для обьявления но всегда присутствуют:
controller - Имя контроллера, без префикса Controller_. Т.е. значение welcome означает контроллер Controller_Welcome.
action - Имя метода, без префикса action_. Например, index - это метод action_index() контроллера.
directory - Необязательный параметр, используйте для указания поддиректории для контроллера. Если указать значение admin, то и контролер должен будет иметь название Controller_Admin_Welcome.
host - Этот параметр определяет имя хоста, который будет обрабатывать запрос, его следует указывать для внешних запросов.
Параметры по умолчанию
Параметры по умолчанию в основном указывают в тех случаях когда в маршруте есть необязательные параметры. Для этого седует указыть их с помощью метода defaults(), как это паказано в нашем контролере по умолчанию
->defaults(array( 'controller' => 'welcome', 'action' => 'index', ));
Так указываются параметры по умолчанию для controller и action. Если не задать параметры по умолчинию в defaults(), то они будут равны NULL (исключением является параметр action, если его не указывать он будет равен index). Поэтому такие адреса как /, welcome, welcome/index будут вести на одну и туже страницу.
Также в defaults() можно указывать параметры которые отсутствуют URI например:
Route::set('contacts', 'contacts(/<action>)') ->defaults(array( 'controller' => 'contacts', 'action' => 'info', ));
Данный маршрут обрабатывает такие адреса contacts, contacts/info и т.д. и в независимости от переданого адреса, будет использовать контролер contacts.
Параметр directory
В настройках роута, помимо указания в массиве таких параметров, как controller и action, есть возможность указывать директорию directory. Это может понядобится для раздела администратора. Часто он прилично отличается по своей структуре от основного сайта, поэтому обычно его контроллеры выносят в отдельную поддиректорию controllers/admin/.
Но дефолтный маршрут обработает адрес /admin/dashboard/, но не так, как мы ожидаем. Параметр controller будет не admin/dashboard, а admin. Соответственно сегмент dashboard будет воспринят как метод.
Чтобы исправить подобные ситуации, необходимо использовать параметр directory:
Route::set('admin', 'admin(/<controller>(/<action>(/<id>)))') ->defaults(array( 'directory' => 'admin', 'controller' => 'dashboard', ));
По итогам анализа данного маршрута по умолчанию будет сгенерировано имя контроллера Controller_Admin_Dashboard, и ожидаемое имя файла controllers/admin/dashboard.php. При этом в вызове defaults() мы указываем имя контроллера без префикса admin_. Система сама его возьмет из параметра directory.
Также можно сделать параметр directory динамическим:
Route::set('directory', '<directory>(/<controller>(/<action>(/<id>)))') ->defaults(array( 'controller' => 'dashboard', ));
Но такой пример является не самый хорошим, т.к. он будет перехватывать практически любой предложенный URI, в том числе и те, которые не используют параметр directory, обычно количестыво таких директорий огранично и заранее известно, например:
Route::set('directory', '<directory>(/<controller>(/<action>(/<id>)))', array('directory' => '(admin|client)')) ->defaults(array( 'controller' => 'dashboard', ));
В данном примере маршрут будет обрабатывать только адреса, начинающиеся с /admin/ и /client/
Регулярные выражения
Для точности задания маршрута используются дополнительные проверки для сегмента. Для этого нужно указать третий аргумент при создании маршрута - массив регулярных выражений для сегментов.
/** разрешаем адреса /user/login/, /user/logout/ и /user/register/ */ Route::set('user', 'user(/<action>)', array('action' => '(login|logout|register)')) ->defaults(array( 'controller' => 'user', ));
Другой пример - маршрут для генерации адресов как в Wordpress:
В следующем примере - маршрут для "календарнрых адресов" как в Wordpress:
/** разрешаем адреса вида /2015/, /2015/03/, /2015/03/04/ и /2015/03/04/nazvanie-statii/ */ Route::set('article', '<year>(/<month>(/<day>(/<title>)))', array( 'year' => '(19|20)\d{2}', // четырехзначное число от 1900 до 2099 'month' => '(0[1-9]|1[012])', // число-месяц от 01 до 09 или от 10 до 12 'day' => '(0[1-9]|[12][0-9]|3[01])', // число-день от 01 до 09 или 10 до 29 или 30 до 31 'title' => '[\d\w\-]+', // название статьи разрешены числа, буквы и дефис )) ->defaults(array( 'controller' => 'article', 'action' => 'list', ));
Порядок обработки
Маршрут должен быть объявлен в начале работы приложения, чтобы принимать участие в анализе URI. Основные такие места - это файл bootstrap.php (там расположен маршрут по умолчанию), а также файлы init.php в директориях модулей. Эти файлы загружаются сразу после ядра фреймворка, поэтому и являются оптимальным местом для размещения роутинга.
Также следует помнить что все маршруты хранятся в стеке по принципу FIFO (первым пришёл — первым ушёл), т.е. чем раньше был добавлен маршрут, тем больший приоритет перед другими он имеет.
Для примера рассмотрим стандартный файл bootstrap.php:
Kohana::modules(array( // 'auth' => MODPATH.'auth', // Basic authentication // 'cache' => MODPATH.'cache', // Caching with multiple backends // 'codebench' => MODPATH.'codebench', // Benchmarking tool 'database' => MODPATH.'database', // Database access // 'image' => MODPATH.'image', // Image manipulation // 'orm' => MODPATH.'orm', // Object Relationship Mapping // 'unittest' => MODPATH.'unittest', // Unit testing 'userguide' => MODPATH.'userguide', // User guide and API documentation )); /** * Set the routes. Each route must have a minimum of a name, a URI and a set of * defaults for the URI. */ Route::set('default', '(<controller>(/<action>(/<id>)))') ->defaults(array( 'controller' => 'welcome', 'action' => 'index', ));
Дефолтный маршрут расположен в самом конце файла bootstrap.php, и он в итоге попадет в самый конец стека. Также в модуле userguide имеется файл init.php, в котором объявлено несколько маршрутов. Как мы помним, файлы init.php выполняются сразу при добавлении модуля. Таким образом, все маршруты модуля userguide будут добавлены раньше, чем маршрут default.
Если же в нескольких модулях имеются объявления маршрутов, то в этой ситуации играет важную роль очередность добавления модулей. В данном примере маршруты модуля database будут более приоритетными, чем userguide.
Впрочем, маршрут может быть добавлен до вызова Kohana::modules(). В этом случае он будет самым первым в стеке.
Хорошая описал работу маршрутизатора.