Контроллеры
11.x
.
Почему это важно?
- Введение
- Простейшие контроллеры
- Определение контроллеров
- Контроллеры и пространства имён
- Контроллеры одного действия
- Посредник контроллера
- Контроллеры ресурсов
- Частичные роуты ресурсов
- Именование роутов ресурса
- Именование параметров роута ресурса
- Локализация URI ресурсов
- Добавление дополнительных роутов в контроллеры ресурсов
- Внедрение зависимостей и контроллеры
- Кэширование роутов
Введение
Вместо того, чтобы определять всю логику обработки запросов в виде замыканий в файлах роутов, вы можете организовать её с помощью классов контроллеров. Контроллеры могут группировать связанную с обработкой HTTP-запросов логику в отдельный класс. Контроллеры хранятся в директории app/Http/Controllers
.
Простейшие контроллеры
Определение контроллеров
Ниже приведён пример простейшего класса контроллера. Обратите внимание, контроллер наследует базовый класс контроллера, встроенный в Laravel. Базовый класс предоставляет несколько удобных методов, таких как метод middleware
, используемый для назначения посредников на действия контроллера:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Показать профиль данного пользователя.
*
* @param int $id
* @return Response
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
Вы можете задать роут для этого контроллера следующим образом:
Route::get('user/{id}', 'UserController@show');
Теперь при соответствии запроса указанному URI роута будет выполняться метод show
класса UserController
. Конечно, параметры роута также будут переданы в метод.
Контроллерам не обязательно наследовать базовый класс. Но тогда у вас не будет таких удобных возможностей, как методы
middleware
,validate
иdispatch
.
Контроллеры и пространства имён
Важно помнить, что при определении роута контроллера нам не надо указывать полное пространство имён контроллера. Так как RouteServiceProvider
загружает файлы вашего роута в группу роута, которая содержит пространство имён, мы указали только ту часть имени класса, которая следует за частью App\Http\Controllers
пространства имён.
Если вы решите разместить свои контроллеры в поддиректориях App\Http\Controllers
, то просто используйте конкретное имя класса относительно корня пространства имён App\Http\Controllers
. Тогда, если полный путь к вашему классу будет App\Http\Controllers\Photos\AdminController
, то вам надо зарегистрировать роуты к контроллеру следующим образом:
Route::get('foo', 'Photos\AdminController@method');
Контроллеры одного действия
Для определения контроллера, обрабатывающего всего одно действие, поместите в контроллер единственный метод __invoke
:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class ShowProfile extends Controller
{
/**
* Показать профиль данного пользователя.
*
* @param int $id
* @return Response
*/
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
При регистрации роутов для контроллеров одного действия вам не надо указывать метод:
Route::get('user/{id}', 'ShowProfile');
Посредник контроллера
Посредников можно назначить роутам контроллера в файлах роутов:
Route::get('profile', 'UserController@show')->middleware('auth');
Но удобнее указать посредника в конструкторе вашего контроллера. Используя метод middleware
в конструкторе контроллера, вы легко можете назначить посредника для действия контроллера. Вы можете даже ограничить использование посредника, назначив его только для определённых методов класса контроллера:
class UserController extends Controller
{
/**
* Создание нового экземпляра контроллера.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
Контроллеры также позволяют регистрировать посредников с помощью замыканий. Это удобный способ определения посредника для одного контроллера, не требующий определения целого класса посредника:
$this->middleware(function ($request, $next) {
// ...
return $next($request);
});
Вы можете назначить посредника на определённый набор действий контроллера, но если возникает такая необходимость, возможно ваш контроллер стал слишком велик. Вместо этого вы можете разбить контроллер на несколько меньших контроллеров.
Контроллеры ресурсов
Маршрутизация ресурсов Laravel назначает обычные CRUD-роуты на контроллеры одной строчкой кода. Например, вы можете создать контроллер, обрабатывающий все HTTP-запросы к фотографиям, хранимым вашим приложением. Вы можете быстро создать такой контроллер с помощью Artisan-команды make:controller
:
php artisan make:controller PhotoController --resource
Эта команда сгенерирует контроллер app/Http/Controllers/PhotoController.php
. Данный контроллер будет содержать метод для каждой доступной операции с ресурсами.
Теперь мы можем зарегистрировать роут контроллера ресурса:
Route::resource('photos', 'PhotoController');
Один этот вызов создаёт множество роутов для обработки различных действий для ресурса. Сгенерированный контроллер уже имеет методы-заглушки для каждого из этих действий с комментариями о том, какие URI и типы запросов они обрабатывают.
Действия, обрабатываемые контроллером ресурсов
Операция | URI | Действие | Название роута |
---|---|---|---|
GET | /photos |
index | photos.index |
GET | /photos/create |
create | photos.create |
POST | /photos |
store | photos.store |
GET | /photos/{photo} |
show | photos.show |
GET | /photos/{photo}/edit |
edit | photos.edit |
PUT/PATCH | /photos/{photo} |
update | photos.update |
DELETE | /photos/{photo} |
destroy | photos.destroy |
Указание модели ресурса
Если вы используете связывание моделей роутов и хотели бы, чтобы методы контроллера ресурсов указывали в качестве аргумента экземпляр модели, можно использовать опцию --model
при генерировании контроллера:
php artisan make:controller PhotoController --resource --model=Photo
Подмена методов формы
Поскольку HTML-формы не могут выполнять запросы PUT
, PATCH
или DELETE
, вам надо добавить скрытое поле _method
для подмены этих HTTP-запросов. Хелпер method_field
создаст это поле для вас:
{{ method_field('PUT') }}
Частичные роуты ресурсов
При объявлении роута вы можете указать подмножество всех возможных действий, которые должен обрабатывать контроллер вместо полного набора стандартных действий:
Route::resource('photo', 'PhotoController', ['only' => [
'index', 'show'
]]);
Route::resource('photo', 'PhotoController', ['except' => [
'create', 'store', 'update', 'destroy'
]]);
Именование роутов ресурса
По умолчанию все действия контроллера ресурсов имеют имена роутов, но вы можете переопределить эти имена, передав массив names
вместе с остальными параметрами:
Route::resource('photo', 'PhotoController', ['names' => [
'create' => 'photo.build'
]]);
Именование параметров роута ресурса
По умолчанию Route::resource
создаст параметры для ваших роутов ресурсов на основе имени ресурса в единственном числе. Это легко можно изменить для каждого ресурса, передав parameters
в массив опций. Массив parameters
должен быть ассоциативным массивом имён ресурсов и имён параметров:
Route::resource('user', 'AdminUserController', ['parameters' => [
'user' => 'admin_user'
]]);
Этот пример генерирует следующие URI для роута ресурса show
:
/user/{admin_user}
Локализация URI ресурсов
По умолчанию Route::resource
будет создавать URI ресурсов, используя английские глаголы. Если вам нужно локализовать глаголы действий create
и edit
, вы можете использовать метод Route::resourceVerbs
. Данную задачу можно выполнить в методе boot
вашего AppServiceProvider
:
use Illuminate\Support\Facades\Route;
/**
* Первоначальная загрузка любых сервисов приложения.
*
* @return void
*/
public function boot()
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
}
Как только глаголы были настроены, регистрация роута ресурса, такая как Route::resource('fotos', 'PhotoController')
, воспроизведет следующие URI:
/fotos/crear
/fotos/{foto}/editar
Добавление дополнительных роутов в контроллеры ресурсов
Если вам надо добавить дополнительные роуты в контроллер ресурсов, не входящие в набор роутов ресурсов по умолчанию, их надо определить до вызова Route::resource
; в ином случае, определенные методом resource
роуты могут нечаянно превзойти по важности ваши дополнительные роуты:
Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');
Старайтесь, чтобы контроллеры были узкоспециализированными. Если вам постоянно требуются методы вне стандартного набора действий с ресурсами, попробуйте разделить контроллер на два небольших контроллера.
Внедрение зависимостей и контроллеры
Внедрение в конструктор
Сервис-контейнер Laravel используется для работы всех контроллеров Laravel. В результате вы можете указывать типы любых зависимостей, которые могут потребоваться вашему контроллеру в его конструкторе. Заявленные зависимости будут автоматически получены и внедрены в экземпляр контроллера:
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Экземпляр репозитория пользователя.
*/
protected $users;
/**
* Создание нового экземпляра контроллера.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
Разумеется, вы можете также указать в качестве аргумента тип любого Laravel-контракта. Если контейнер может с ним работать, значит вы можете указывать его тип. В некоторых случаях внедрение зависимостей в контроллер обеспечивает лучшую тестируемость приложения.
Внедрение в метод
Кроме внедрения в конструктор, вы также можете указывать типы зависимостей в методах вашего контроллера. Распространённый пример внедрения в метод — внедрение экземпляра Illuminate\Http\Request
в один из методов контроллера:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Хранить нового пользователя.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
Если метод вашего контроллера также ожидает данные из параметра роута, просто перечислите аргументы роута после остальных зависимостей. Например, если ваш роут определён так:
Route::put('user/{id}', 'UserController@update');
Вы по-прежнему можете указать тип Illuminate\Http\Request
в качестве аргумента и обращаться к параметру id
, определив метод контроллера как указано ниже:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Обновить данного пользователя.
*
* @param Request $request
* @param string $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
}
Кэширование роутов
Роуты на основе замыканий нельзя кэшировать. Чтобы использовать кэширование роутов, необходимо перевести все роуты замыканий на классы контроллера.
Если ваше приложение единолично использует роуты контроллера, то вы можете воспользоваться преимуществом кэширования роутов в Laravel. Использование кэша роутов радикально уменьшит время, требуемое для регистрации всех роутов вашего приложения. В некоторых случаях регистрация ваших роутов может стать быстрее в 100 раз. Для создания кэша роутов просто выполните Artisan-команду route:cache
:
php artisan route:cache
После выполнения этой команды ваши кэшированные роуты будут загружаться при каждом запросе. Помните, после добавления новых роутов, вам необходимо заново сгенерировать свежий кэш роутов. Поэтому нужно выполнить команду route:cache
уже при развёртывании вашего проекта.
Для очистки кэша роутов используйте команду route:clear
:
php artisan route:clear