Контролери
- Вступ
- Написання контролерів
- Контролер Middleware
- Ресурсні контролери
- Ін'єкція залежностей і Контролери
Вступ
Замість визначення всієї логіки обробки запитів як замикань у ваших файлах маршрутів, ви можете організувати цю поведінку, використовуючи класи "контролерів". Контролери можуть групувати пов'язану логіку обробки запитів у єдиний клас. Наприклад, клас 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 буде викликано, і параметри маршруту будуть передані до методу.
Контролери не вимагають розширювати базовий клас. Однак іноді зручно розширити базовий клас контролера, який містить методи, що повинні бути спільними для всіх ваших контролерів.
Контролери з однією дією
Якщо дія контролера є особливо складною, ви можете знайти зручним присвятити цілий клас контролера цій одній дії. Щоб досягти цього, ви можете визначити єдиний метод __invoke у контролері:
<?php namespace App\Http\Controllers; class ProvisionServer extends Controller { /** * Розгорнути новий вебсервер. */ public function __invoke() { // ... } }
Коли реєструєте маршрути для контролерів з однією дією, вам не потрібно вказувати метод контролера. Натомість, ви можете просто передати ім'я контролера маршрутизатору:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
Ви можете згенерувати викликаємий контролер, використовуючи опцію --invokable команди Artisan make:controller:
php artisan make:controller ProvisionServer --invokable
Шаблони контролерів можуть бути налаштовані за допомогою публікації шаблонів.
Контролер Middleware
Middleware може бути призначено маршрутам контролера у ваших файлах маршрутів:
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
Або, вам може бути зручно вказати middleware у вашому класі контролера. Для цього ваш контролер повинен реалізувати інтерфейс HasMiddleware, який вимагає, щоб контролер мав статичний метод middleware. З цього методу ви можете повернути масив middleware, які повинні бути застосовані до дій контролера:
<?php namespace App\Http\Controllers; use Illuminate\Routing\Controllers\HasMiddleware; use Illuminate\Routing\Controllers\Middleware; class UserController extends Controller implements HasMiddleware { /** * Отримати middleware, яке слід призначити контролеру. */ public static function middleware(): array { return [ 'auth', new Middleware('log', only: ['index']), new Middleware('subscribed', except: ['store']), ]; } // ... }
Ви також можете визначити middleware контролера як замикання, що надає зручний спосіб визначити вбудоване middleware без написання цілого класу middleware:
use Closure; use Illuminate\Http\Request; /** * Отримати middleware, яке слід призначити контролеру. */ public static function middleware(): array { return [ 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 |
Налаштування поведінки відсутньої моделі
Зазвичай, буде згенеровано 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');
});
М'яко видалені моделі
Зазвичай, неявне зв'язування моделей не буде отримувати моделі, які були м'яко видалені, і натомість поверне 404 HTTP-відповідь. Однак, ви можете вказати фреймворку дозволити м'яко видалені моделі, викликавши метод 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\AppServiceProvider вашого застосунку:
/** * Ініціалізувати будь-які сервіси застосунку. */ 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);
Пам'ятайте, що ваші контролери повинні бути зосередженими. Якщо ви виявите, що вам регулярно потрібні методи поза типовим набором дій ресурсу, розгляньте можливість розділення вашого контролера на два менших контролери.
Контролери ресурсів Singleton
Іноді ваш застосунок матиме ресурси, які можуть мати лише один екземпляр. Наприклад, "профіль" користувача може бути відредагований або оновлений, але користувач не може мати більше одного "профілю". Так само зображення може мати один "мініатюру". Ці ресурси називаються "синглтон ресурсами", що означає, що може існувати лише один екземпляр ресурсу. У таких випадках ви можете зареєструвати контролер "синглтон" ресурсу:
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 |
Ресурси Singleton, що можна створювати
Іноді ви можете захотіти визначити маршрути створення та зберігання для singleton ресурсу. Щоб досягти цього, ви можете викликати метод creatable при реєстрації маршруту singleton ресурсу:
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();
Ресурси Singleton API
Метод apiSingleton може бути використаний для реєстрації сінглтон-ресурсу, який буде оброблятися через API, таким чином роблячи маршрути create та edit непотрібними:
Route::apiSingleton('profile', ProfileController::class);
Звичайно, API сінглтон ресурси також можуть бути створюваними, що зареєструє маршрути store і destroy для ресурсу:
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
Middleware і Ресурсні Контролери
Laravel дозволяє призначати middleware для всіх або лише для конкретних методів ресурсних маршрутів, використовуючи методи middleware, middlewareFor та withoutMiddlewareFor. Ці методи забезпечують детальний контроль над тим, яке middleware застосовується до кожної дії ресурсу.
Застосування middleware до всіх методів
Ви можете використовувати метод middleware для призначення middleware для всіх маршрутів, згенерованих ресурсним або одиничним ресурсним маршрутом:
Route::resource('users', UserController::class)
->middleware(['auth', 'verified']);
Route::singleton('profile', ProfileController::class)
->middleware('auth');
Застосування Middleware до конкретних методів
Ви можете використовувати метод middlewareFor для призначення middleware для одного або декількох конкретних методів заданого ресурсного контролера:
Route::resource('users', UserController::class)
->middlewareFor('show', 'auth');
Route::apiResource('users', UserController::class)
->middlewareFor(['show', 'update'], 'auth');
Route::resource('users', UserController::class)
->middlewareFor('show', 'auth')
->middlewareFor('update', 'auth');
Route::apiResource('users', UserController::class)
->middlewareFor(['show', 'update'], ['auth', 'verified']);
Метод middlewareFor також може бути використаний разом з контролерами ресурсів singleton та API singleton:
Route::singleton('profile', ProfileController::class)
->middlewareFor('show', 'auth');
Route::apiSingleton('profile', ProfileController::class)
->middlewareFor(['show', 'update'], 'auth');
Виключення Middleware з певних методів
Ви можете використовувати метод withoutMiddlewareFor, щоб виключити middleware з певних методів ресурсного контролера:
Route::middleware(['auth', 'verified', 'subscribed'])->group(function () {
Route::resource('users', UserController::class)
->withoutMiddlewareFor('index', ['auth', 'verified'])
->withoutMiddlewareFor(['create', 'store'], 'verified')
->withoutMiddlewareFor('destroy', 'subscribed');
});
Ін'єкція залежностей і Контролери
Ін'єкція конструктора
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'); } }
