Неочевидные фишки маршрутизации контроллеров Laravel 3
Недавно столкнулся с одной интересной особенностью роутинга в Laravel 3, который в документации слабо освещен.
Я использую Laravel 3 по всем канонам MVC. То есть вся логика работы с базой данных находится в моделях, представления отвечают за вывод данных, а контроллеры управляют всем этим процессом. Laravel 3 позволяет обходиться вообще без контроллеров — но такой подход мне не нравится.
Итак, чтобы действия контроллеров стали доступны для исполнения, их нужно прописать в роутах. Для этого есть несколько способов.
1. Вручную прописать действие
Route::any('test/page', array('uses' => 'test@page')); |
Не самый удобный вариант… Подойдет лишь тогда, когда нужно сделать красивый url для какого-нибудь действия.
2. Зарегистрировать все действия контроллера
Route::controller('test'); |
В этом случае фреймворк проанализирует контроллер test и сделает доступными для доступа правильные методы (созданные по правилам именования, в зависимости от типа контроллера: обычный или restfull).
Данный подход похож на работу с роутами контроллеров у старых фреймворков, таких как CodeIgniter или Kohana.
3. Зарегистрировать все действия всех контроллеров
Route::controller(Controller::detect()); |
Чтобы не регистрировать каждый контроллер существует эта удобная конструкция.
А теперь собственно о том, что ввело меня в ступор.
Есть пустое приложение Laravel 3.
Добавляем контроллер test.
// application/controllers/test.php class Test_Controller extends Base_Controller { public function get_index($id = false) { echo 'test get_index, id: '; var_dump($id); } public function get_welcome() { echo 'test get_welcome'; } } |
В роутах сразу пишем:
// application/routes.php Route::controller(Controller::detect()); Route::any('test/(:any?)', array('uses' => 'test@index')); |
Пробуем зайти по разным URL:
/test
— открывает левую страницу, не относящуюся к контроллеру Test
/test/index
— открывает действие index контроллера Test
/test/index/123
— открывает действие index контроллера Test, передает в параметр $id = 123
/test/welcome
— открывает действие welcome контроллера Test
/test/abc
— 404 ошибка
Что меня не устраивает:
– Если набрать просто адрес контроллера — до самого контроллера не доходит запрос
— Произвольный роут не срабатывает
А всё потому, что мы прописали Route::controller(Controller::detect())
в самом начале файла маршрутов, и произвольные маршруты для того же самого контроллера не срабатывают.
Если мы вместо test/(:any?)
укажем имя любого другого, несуществующего контроллера, то произвольный роутинг заработает как надо.
Route::controller(Controller::detect()); Route::any('test2/(:any?)', array('uses' => 'test@index')); |
/test2
— ооткрывает действие index контроллера Test
/test2/index
— открывает действие index контроллера Test, передает в параметр $id = index
/test2/index/123
— открывает действие index контроллера Test, передает в параметр $id = 123
/test2/welcome
— открывает действие index контроллера Test, передает в параметр $id = welcome
/test2/abc
— открывает действие index контроллера Test, передает в параметр $id = abc
Давайте разберёмся, почему так получается. При вызове Route::controller('test')
фреймворк добавляет свой роут, который перехватывает все обращения к контроллеру test, ожидая получить из URL controller/action
. До других роутов, начинающихся с test, дело не дойдет.
Указывая роут, начинающийся с test2, мы не попадаем под действие стандартного роута.
Если мы просто перенесем определение всех действий контроллеров в самый низ, то наш роут сработает в первую очередь.
Route::any('test/(:any?)', array('uses' => 'test@index')); Route::controller(Controller::detect()); |
Второй неочевидный момент в механике работы с маршрутизацией.
Указав в шаблоне (:any?) вторым сегментом, мы заставим перехватывать любые обращения к этому сегменту. То есть до выполнения методов самого контроллера дело не дойдет. Тут или использовать маршрутизацию вида /controller/action или переадресацию второго параметра.
Есть компромисс. Обычно редко используют действия с числовым именованием. Поэтому можно писать такой маршрут:
Route::any('test/(:num?)', array('uses' => 'test@index')); Route::controller(Controller::detect()); |
/test
— открывает действие index контроллера Test
/test/index
— открывает действие index контроллера Test
/test/index/123
— открывает действие index контроллера Test, передает в параметр $id = 123
/test/welcome
— открывает действие welcome контроллера Test
/test/abc
— 404 ошибка
/test/123
— открывает действие index контроллера Test, передает в параметр $id = 123
Давненько не писал ты про Ларавель, хотя в предыдущих статьях неоднократно упоминал, что этот фреймворк развивается быстро и новые версии выходят часто.
Как насчет того, чтобы осветить фишки последней стабильной версии, все-таки он уже очень далеко ушел от рассматривавшейся здесь 3 версии? В частности, порядок регистрации роутов, который в свежем Ларавеле тоже имеет значение, как ты буквально на днях заметил на практике.