- Введение
- Написание контроллеров
- Посредник контроллера
- Ресурсные контроллеры
- Внедрение зависимостей и контроллеры
Вместо того, чтобы определять всю логику обработки запросов как замыкания в файлах маршрутов, вы можете организовать это поведение с помощью классов «контроллеров». Контроллеры могут сгруппировать связанную логику обработки запросов в один класс. Например, класс UserController
может обрабатывать все входящие запросы, относящиеся к пользователям, включая отображение, создание, обновление и удаление пользователей. По умолчанию контроллеры хранятся в каталоге app/Http/Controllers
.
Давайте посмотрим на отвлеченный пример контроллера. Обратите внимание, что он расширяет базовый класс контроллера App\Http\Controllers\Controller
, включенный в Laravel:
<?php
namespace App\Http\Controllers;
use App\Models\User;
class UserController extends Controller
{
/**
* Показать профиль конкретного пользователя.
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}
Вы можете определить маршрут к этому методу контроллера следующим образом:
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
Когда входящий запрос совпадает с указанным URI маршрута, будет вызван метод show
класса App\Http\Controllers\UserController
, и параметры маршрута будут переданы методу.
Примечание
Контроллеры не требуют расширения базового класса. Однако у вас не будет доступа к удобным функциям, таким как методыmiddleware
иauthorize
.
Если действие контроллера является особенно сложным, вам может показаться удобным посвятить целый класс контроллера этому единственному действию. Для этого вы можете определить один метод __invoke
в контроллере:
<?php
namespace App\Http\Controllers;
use App\Models\User;
class ProvisionServer extends Controller
{
/**
* Подготовить новый веб-сервер.
*
* @return \Illuminate\Http\Response
*/
public function __invoke()
{
// ...
}
}
При регистрации маршрутов для контроллеров одиночного действия вам не нужно указывать метод контроллера. Вместо этого вы можете просто передать маршрутизатору имя контроллера:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
Вы можете сгенерировать вызываемый контроллер, используя параметр --invokable
команды make:controller
Artisan:
php artisan make:controller ProvisionServer --invokable
Примечание
Заготовки контроллера можно настроить с помощью публикации заготовок.
Посредник может быть назначен маршрутам контроллера в ваших файлах маршрутизации:
Route::get('profile', [UserController::class, '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);
});
Если вы думаете о каждой модели Eloquent в вашем приложении как о «ресурсе», то для каждого ресурса в вашем приложении обычно выполняются одни и те же наборы действий. Например, представьте, что ваше приложение содержит модель Photo
и модель Movie
. Вполне вероятно, что пользователи могут создавать, читать, обновлять или удалять эти ресурсы.
Благодаря такому распространенному варианту использования, маршрутизация ресурсов Laravel присвоит типичные маршруты создания, чтения, обновления и удаления («CRUD») контроллеру с помощью одной строки кода. Для начала мы можем использовать параметр --resource
команды make:controller
Artisan, чтобы быстро создать контроллер для обработки этих действий:
php artisan make:controller PhotoController --resource
Эта команда поместит новый класс контроллера в каталог app/Http/Controllers
вашего приложения. Контроллер будет содержать метод для каждого из доступных действий с ресурсами. Затем, вы можете зарегистрировать маршрут ресурса, который указывает на контроллер:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);
Это единое определение маршрута создаст несколько маршрутов для обработки множества действий с ресурсом. Сгенерированный контроллер уже будет иметь заготовки для каждого из этих действий. Помните, вы всегда можете получить быстрый обзор маршрутов своего приложения, выполнив команду route:list
Artisan.
Вы даже можете зарегистрировать сразу несколько контроллеров ресурсов, передав массив методу resources
:
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Метод | 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 |
Обычно, если неявно связанная модель ресурса не найдена, то генерируется 404
HTTP-ответ. Однако вы можете изменить это поведение, вызвав метод missing
при определении вашего ресурсного маршрута. Метод missing
принимает замыкание, которое будет вызываться, если неявно связанная модель не может быть найдена для любого из маршрутов ресурса:
use App\Http\Controllers\PhotoController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::resource('photos', PhotoController::class)
->missing(function (Request $request) {
return Redirect::route('photos.index');
});
Как правило, неявная привязка модели не извлекает модели, которые были программно удалены, а вместо этого возвращает HTTP-ответ 404. Тем не менее, вы можете указать Laravel разрешить извлечение таких моделей, вызвав метод withTrashed
при определении маршрута ресурса:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->withTrashed();
Вызов withTrashed
без аргументов позволит извлечение таких моделей для маршрутов show
, edit
и update
ресурса. Вы можете указать подмножество этих маршрутов, передав массив методу withTrashed
:
Route::resource('photos', PhotoController::class)->withTrashed(['show']);
Если вы используете привязку модели к маршруту и хотите, чтобы методы контроллера ресурса содержали типизацию экземпляра модели, вы можете использовать параметр --model
при создании контроллера:
php artisan make:controller PhotoController --model=Photo --resource
Вы можете указать флаг --requests
при создании ресурсного контроллера, чтобы указать Artisan о попутном создании классов запросов для методов store
и update
контроллера:
php artisan make:controller PhotoController --model=Photo --resource --requests
При объявлении маршрута ресурса вы можете указать подмножество действий, которые должен обрабатывать контроллер, вместо полного набора действий по умолчанию:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
Route::resource('photos', PhotoController::class)->except([
'create', 'store', 'update', 'destroy'
]);
При определении маршрутов ресурса, которые будут использоваться API, бывает необходимо исключить маршруты, содержащие ответы с HTML-шаблонами, такие как create
и edit
. Для удобства вы можете использовать метод apiResource
, чтобы автоматически исключить эти два маршрута:
use App\Http\Controllers\PhotoController;
Route::apiResource('photos', PhotoController::class);
Вы можете зарегистрировать сразу несколько ресурсных API-контроллеров, передав массив методу apiResources
:
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Чтобы быстро сгенерировать ресурсный API-контроллер, который не включает методы create
или edit
, используйте переключатель --api
при выполнении команды make:controller
:
php artisan make:controller PhotoController --api
Иногда требуется определить маршруты ко вложенному ресурсу. Например, фоторесурс может иметь несколько комментариев, которые могут быть прикреплены к фотографии. Чтобы вложить ресурсные контроллеры, используйте «точечную нотацию» в определении маршрута:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
Этот маршрут зарегистрирует вложенный ресурс, к которому можно получить доступ с помощью URI, подобных следующим:
/photos/{photo}/comments/{comment}
Функционал неявной привязки модели Laravel может автоматически ограничивать вложенные привязки для подтверждения принадлежности извлеченной дочерней модели по отношению к родительской модели. Используя метод scoped
при определении вашего вложенного ресурса, вы можете включить автоматическое ограничение, а также указать Laravel, через какое поле дочерний ресурс должен быть получен. Для получения дополнительных сведений о том, как это сделать, смотрите документацию по ограничению ресурсных маршрутов.
Часто нет необходимости иметь в URI и родительский, и дочерний идентификаторы, поскольку дочерний идентификатор уже является уникальным идентификатором. При использовании уникальных идентификаторов, таких как автоинкрементные первичные ключи, для идентификации ваших моделей в сегментах URI, вы можете использовать «упрощенное вложение»:
use App\Http\Controllers\CommentController;
Route::resource('photos.comments', CommentController::class)->shallow();
Это объявление маршрута будет определять следующие маршруты:
Метод | URI | Действие | Имя маршрута |
---|---|---|---|
GET | /photos/{photo}/comments |
index | photos.comments.index |
GET | /photos/{photo}/comments/create |
create | photos.comments.create |
POST | /photos/{photo}/comments |
store | photos.comments.store |
GET | /comments/{comment} |
show | comments.show |
GET | /comments/{comment}/edit |
edit | comments.edit |
PUT/PATCH | /comments/{comment} |
update | comments.update |
DELETE | /comments/{comment} |
destroy | comments.destroy |
По умолчанию все действия ресурсного контроллера имеют имя маршрута; однако, вы можете переопределить эти имена, передав массив имен с желаемыми именами маршрутов:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->names([
'create' => 'photos.build'
]);
По умолчанию Route::resource
создаст параметры маршрута для ваших ресурсных маршрутов на основе «сингулярной» версии имени ресурса. Вы можете легко переопределить это для каждого ресурса, используя метод parameters
. Массив, передаваемый в метод parameters
, должен быть ассоциативным массивом имен ресурсов и имен параметров:
use App\Http\Controllers\AdminUserController;
Route::resource('users', AdminUserController::class)->parameters([
'users' => 'admin_user'
]);
В приведенном выше примере создается следующий URI для маршрута show
ресурса:
/users/{admin_user}
Функционал ограничения неявной привязки модели Laravel может автоматически ограничивать вложенные привязки для подтверждения принадлежности извлеченной дочерней модели по отношению к родительской модели. Используя метод scoped
при определении вашего вложенного ресурса, вы можете включить автоматическое ограничение, а также указать Laravel, через какое поле дочерний ресурс должен быть получен:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);
Этот маршрут зарегистрирует ограниченный вложенный ресурс, к которому можно получить доступ с помощью таких URI, как следующий:
/photos/{photo}/comments/{comment:slug}
При использовании пользовательской неявной привязки с ключом в качестве параметра вложенного маршрута, Laravel автоматически задает ограничение для получения вложенной модели своим родителем, используя соглашения, чтобы угадать имя отношения родительского элемента. В этом случае предполагается, что модель Photo
имеет отношение с именем comments
(множественное число от имени параметра маршрута), которое можно использовать для получения модели Comment
.
По умолчанию Route::resource
создает URI ресурсов с использованием английских глаголов и языковых правил множественного числа. Если вам нужно локализовать команды действий create
и edit
, то вы можете использовать метод Route::resourceVerbs
. Это можно сделать в начале метода boot
поставщика App\Providers\RouteServiceProvider
:
/**
* Определить связывание модели и маршрута, фильтры шаблонов и т.д.
*
* @return void
*/
public function boot()
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
// ...
}
Построитель слов во множественном числе Laravel поддерживает несколько различных языков, которые вы можете изменить в зависимости от ваших потребностей. После того, как глаголы были скорректированы, а также изменены языковые правила множественного числа, регистрация маршрута ресурса, например, Route::resource('publicacion', PublicacionController::class)
, создаст следующие URI:
/publicacion/crear
/publicacion/{publicaciones}/editar
Если вам нужно добавить дополнительные маршруты ресурсного контроллера помимо набора ресурсных маршрутов по умолчанию, вы должны определить эти маршруты перед вызовом метода Route::resource
; в противном случае маршруты, определенные методом resource
, могут непреднамеренно иметь приоритет над вашими дополнительными маршрутами:
use App\Http\Controller\PhotoController;
Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);
Примечание
Помните, что ваши контроллеры должны быть сосредоточенными. Если вам постоянно требуются методы, выходящие за рамки типичного набора действий с ресурсами, рассмотрите возможность разделения вашего контроллера на два меньших контроллера.
Иногда ваше приложение будет иметь ресурсы, содержащие только один экземпляр. Например, «профиль» пользователя можно редактировать или обновлять, но у пользователя не может быть более одного «профиля». Точно так же изображение может иметь одну «миниатюру». Эти ресурсы называются «одиночными ресурсами», что означает, что может существовать один и только один экземпляр ресурса. В этих сценариях вы можете зарегистрировать контроллер «одиночного» ресурса:
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::singleton('profile', ProfileController::class);
Приведенное выше определение одиночного ресурса зарегистрирует следующие маршруты. Как видите, маршруты «создания» не регистрируются для одиночных ресурсов, а зарегистрированные маршруты не принимают идентификатор, поскольку может существовать только один экземпляр ресурса:
Метод | URI | Действие | Имя маршрута |
---|---|---|---|
GET | /profile |
show | profile.show |
GET | /profile/edit |
edit | profile.edit |
PUT/PATCH | /profile |
update | profile.update |
Одиночные ресурсы также могут быть вложены в стандартный ресурс:
Route::singleton('photos.thumbnail', ThumbnailController::class);
В этом примере ресурс photos
получит все стандартные ресурсные маршруты; однако ресурс thumbnail
будет одиночным ресурсом со следующими маршрутами:
Метод | URI | Действие | Имя маршрута |
---|---|---|---|
GET | /photos/{photo}/thumbnail |
show | photos.thumbnail.show |
GET | /photos/{photo}/thumbnail/edit |
edit | photos.thumbnail.edit |
PUT/PATCH | /photos/{photo}/thumbnail |
update | photos.thumbnail.update |
Иногда требуется определить маршруты создания и сохранения для одиночного ресурса. Для этого вы можете вызвать метод creatable
при регистрации маршрутов одиночного ресурса:
Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();
В этом примере будут зарегистрированы следующие маршруты. Для расширенных одиночных ресурсов также будет зарегистрирован маршрут DELETE
:
Метод | URI | Действие | Имя маршрута |
---|---|---|---|
GET | /photos/{photo}/thumbnail/create |
create | photos.thumbnail.create |
POST | /photos/{photo}/thumbnail |
store | photos.thumbnail.store |
GET | /photos/{photo}/thumbnail |
show | photos.thumbnail.show |
GET | /photos/{photo}/thumbnail/edit |
edit | photos.thumbnail.edit |
PUT/PATCH | /photos/{photo}/thumbnail |
update | photos.thumbnail.update |
DELETE | /photos/{photo}/thumbnail |
destroy | photos.thumbnail.destroy |
Если вы хотите, чтобы Laravel зарегистрировал маршрут DELETE
для одиночного ресурса, но не регистрировал маршруты создания или сохранения, то вы можете использовать метод destroyable
:
Route::singleton(...)->destroyable();
Метод apiSingleton
может использоваться для регистрации одиночного ресурса API, исключающим маршруты create
и edit
:
Route::apiSingleton('profile', ProfileController::class);
Конечно, одиночные ресурсы API также могут быть расширенными, содержать маршруты store
и destroy
ресурса:
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
Контейнер служб Laravel используется для извлечения всех контроллеров. В результате вы можете объявить любые зависимости, которые могут понадобиться вашему контроллеру в его конструкторе. Объявленные зависимости будут автоматически извлечены и внедрены в экземпляр контроллера:
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Экземпляр репозитория Пользователь.
*/
protected $users;
/**
* Создать новый экземпляр контроллера.
*
* @param \App\Repositories\UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
Помимо внедрения в конструкторе, вы также можете объявить тип зависимости в методах вашего контроллера. Распространенный вариант использования внедрения в методе – это внедрение экземпляра Illuminate\Http\Request
в методы вашего контроллера:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Сохранить нового пользователя.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
Если ваш метод контроллера также ожидает входные данные из параметра маршрута, укажите аргументы маршрута после других зависимостей. Например, если ваш маршрут определен так:
use App\Http\Controllers\UserController;
Route::put('/user/{id}', [UserController::class, 'update']);
Вы по-прежнему можете объявить тип зависимости Illuminate\Http\Request
и получить доступ к вашему параметру id
, определив свой метод контроллера следующим образом:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Обновить конкретного пользователя.
*
* @param \Illuminate\Http\Request $request
* @param string $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
}