Неочевидные фишки маршрутизации контроллеров Laravel 3

30th Июнь 2013 | Категории: Laravel | Метки: ,

Недавно столкнулся с одной интересной особенностью роутинга в 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

Subscribe without commenting


  1. Мария
    28th Август 2015 в 04:02

    Давненько не писал ты про Ларавель, хотя в предыдущих статьях неоднократно упоминал, что этот фреймворк развивается быстро и новые версии выходят часто.
    Как насчет того, чтобы осветить фишки последней стабильной версии, все-таки он уже очень далеко ушел от рассматривавшейся здесь 3 версии? В частности, порядок регистрации роутов, который в свежем Ларавеле тоже имеет значение, как ты буквально на днях заметил на практике.