1. Основы
  2. Контроллеры

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

Вместо того чтобы определять всю логику обработки запросов в виде замыканий в файлах маршрутов, вы можете захотеть организовать это поведение с использованием классов "контроллера". Контроллеры могут группировать связанную логику обработки запросов в единый класс. Например, класс UserController может обрабатывать все входящие запросы, связанные с пользователями, включая отображение, создание, обновление и удаление пользователей. По умолчанию контроллеры хранятся в каталоге app/Http/Controllers.

Написание контроллеров

Основные контроллеры

Чтобы быстро создать новый контроллер, вы можете выполнить команду Artisan make:controller. По умолчанию все контроллеры для вашего приложения хранятся в каталоге app/Http/Controllers:

php artisan make:controller UserController

Давайте рассмотрим пример базового контроллера. Контроллер может иметь любое количество общедоступных методов, которые будут отвечать на входящие HTTP-запросы:

<?php
 
namespace App\Http\Controllers;
 
use App\Models\User;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Показать профиль для указанного пользователя.
*/
public function show(string $id): View
{
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;
 
class ProvisionServer extends Controller
{
/**
* Предоставьте новый веб-сервер.
*/
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
{
/**
* Создайте новый экземпляр контроллера.
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}

Контроллеры также позволяют вам регистрировать промежуточное ПО, используя замыкание. Это предоставляет удобный способ определения встроенного промежуточного ПО для одного контроллера без создания целого класса промежуточного ПО:

use Closure;
use Illuminate\Http\Request;
 
$this->middleware(function (Request $request, Closure $next) {
return $next($request);
});

Контроллеры ресурсов

Если вы думаете о каждой модели Eloquent в вашем приложении как о "ресурсе", обычно выполняется один и тот же набор действий для каждого ресурса в вашем приложении. Например, представьте, что ваше приложение содержит модели Photo и Movie. Скорее всего, пользователи могут создавать, читать, обновлять или удалять эти ресурсы.

Из-за этого общего случая использования маршрутизации ресурсов Laravel автоматически назначает типичные маршруты создания, чтения, обновления и удаления ("CRUD") контроллеру одной строкой кода. Для начала мы можем использовать опцию --resource команды Artisan make:controller, чтобы быстро создать контроллер для обработки этих действий:

php artisan make:controller PhotoController --resource

Эта команда создаст контроллер по адресу app/Http/Controllers/PhotoController.php. В контроллере будут содержаться методы для каждой из доступных операций с ресурсом. Затем вы можете зарегистрировать маршрут ресурса, указав контроллер:

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class);

Это единственное определение маршрута создает несколько маршрутов для обработки различных действий с ресурсом. Сгенерированный контроллер уже будет содержать заготовки для каждого из этих действий. Помните, что вы всегда можете получить быстрый обзор маршрутов вашего приложения, запустив команду Artisan route:list.

Вы можете зарегистрировать множество контроллеров ресурсов сразу, передав массив методу 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

Настройка поведения отсутствующей модели

Обычно будет сгенерирован HTTP-ответ 404, если не удается неявно связать модель ресурса. Однако вы можете настроить это поведение, вызвав метод 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. Однако вы можете научить фреймворк обрабатывать мягко удаленные модели, вызвав метод 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 на генерацию классов формных запросов для методов хранения и обновления контроллера:

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

При объявлении маршрутов ресурсов, которые будут использоваться в 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

Наименование маршрутов ресурсов

По умолчанию у всех действий контроллера ресурсов есть имя маршрута; однако вы можете переопределить эти имена, передав массив names с вашими желаемыми именами маршрутов:

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.

Локализация URI ресурсов

По умолчанию Route::resource создает URI ресурсов, используя английские глаголы и правила множественного числа. Если вам нужно локализовать глаголы действий create и edit, вы можете использовать метод Route::resourceVerbs. Это можно сделать в начале метода boot в вашем App\Providers\RouteServiceProvider приложения:

/**
* Определите привязки модели к маршруту, фильтры шаблона и т.д.
*/
public function boot(): void
{
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();

API одиночные ресурсы

Метод apiSingleton можно использовать для регистрации одиночного ресурса, который будет обрабатываться через API, тем самым избавляясь от необходимости регистрации маршрутов create и edit:

Route::apiSingleton('profile', ProfileController::class);

Конечно же, API-ресурсы-одиночки также могут быть creatable, что приведет к регистрации маршрутов store и destroy для ресурса:

Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();

Внедрение зависимостей и контроллеры

Внедрение через конструктор

В Laravel контейнере служб используется для разрешения всех контроллеров Laravel. В результате вы можете указать зависимости, которые ваш контроллер может потребовать в своем конструкторе. Объявленные зависимости будут автоматически разрешаться и внедряться в экземпляр контроллера:

<?php
 
namespace App\Http\Controllers;
 
use App\Repositories\UserRepository;
 
class UserController extends Controller
{
/**
* Создайте новый экземпляр контроллера.
*/
public function __construct(
protected UserRepository $users,
) {}
}

Внедрение через метод

Помимо инъекции через конструктор, вы также можете указывать зависимости в методах вашего контроллера. Обычным случаем использования для инъекции метода является внедрение экземпляра Illuminate\Http\Request в методы вашего контроллера:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class UserController extends Controller
{
/**
* Сохраните нового пользователя.
*/
public function store(Request $request): RedirectResponse
{
$name = $request->name;
 
// Сохраните пользователя...
 
return redirect('/users');
}
}

Если ваш метод контроллера также ожидает ввод из параметра маршрута, укажите свои аргументы маршрута после других зависимостей. Например, если ваш маршрут определен следующим образом:

use App\Http\Controllers\UserController;
 
Route::put('/user/{id}', [UserController::class, 'update']);

Вы все равно можете указать тип Illuminate\Http\Request и получить доступ к вашему параметру id, определив метод контроллера следующим образом:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class UserController extends Controller
{
/**
* Обновите указанного пользователя.
*/
public function update(Request $request, string $id): RedirectResponse
{
// Обновите пользователя...
 
return redirect('/users');
}
}