コントローラ
イントロダクション
ルートファイル内のクロージャとしてすべてのリクエスト処理ロジックを定義する代わりに、「コントローラ」クラスを使用してこの動作を整理したいと思うかもしれません。コントローラは、関連するリクエスト処理ロジックを単一のクラスにグループ化できます。たとえば、UserControllerクラスは、ユーザーの表示、作成、更新、削除など、ユーザーに関連するすべての受信リクエストを処理します。デフォルトでは、コントローラはapp/Http/Controllersディレクトリに保存されます。
コントローラの記述
基本のコントローラ
新しいコントローラをすばやく生成するには、make:controller Artisanコマンドを実行します。デフォルトでは、アプリケーションのすべてのコントローラはapp/Http/Controllersディレクトリに保存されます。
1php artisan make:controller UserController
基本的なコントローラの例を見てみましょう。コントローラには、受信HTTPリクエストに応答するpublicメソッドをいくつでも含められます。
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Models\User; 6use Illuminate\View\View; 7 8class UserController extends Controller 9{10 /**11 * Show the profile for a given user.12 */13 public function show(string $id): View14 {15 return view('user.profile', [16 'user' => User::findOrFail($id)17 ]);18 }19}
コントローラクラスとメソッドを記述したら、次のようにコントローラメソッドへのルートを定義できます。
1use App\Http\Controllers\UserController;2 3Route::get('/user/{id}', [UserController::class, 'show']);
受信リクエストが指定されたルートURIと一致すると、App\Http\Controllers\UserControllerクラスのshowメソッドが呼び出され、ルートパラメータがメソッドに渡されます。
コントローラは、基本クラスを拡張する必要はありません。ただし、すべてのコントローラで共有する必要があるメソッドを含む基本コントローラクラスを拡張すると便利な場合があります。
シングルアクションコントローラ
コントローラのアクションが特に複雑な場合は、その単一のアクションにコントローラクラス全体を割り当てると便利な場合があります。これを実現するには、コントローラ内に単一の__invokeメソッドを定義します。
1<?php 2 3namespace App\Http\Controllers; 4 5class ProvisionServer extends Controller 6{ 7 /** 8 * Provision a new web server. 9 */10 public function __invoke()11 {12 // ...13 }14}
シングルアクションコントローラのルートを登録するときは、コントローラメソッドを指定する必要はありません。代わりに、コントローラの名前をルータに渡すだけです。
1use App\Http\Controllers\ProvisionServer;2 3Route::post('/server', ProvisionServer::class);
make:controller Artisanコマンドの--invokableオプションを使用して、呼び出し可能なコントローラを生成できます。
1php artisan make:controller ProvisionServer --invokable
コントローラのスタブは、スタブの公開を使用してカスタマイズできます。
コントローラミドルウェア
ミドルウェアは、ルートファイルでコントローラのルートに割り当てられます。
1Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
または、コントローラクラス内でミドルウェアを指定すると便利な場合があります。これを行うには、コントローラはHasMiddlewareインターフェイスを実装する必要があります。これは、コントローラが静的なmiddlewareメソッドを持つべきであることを示します。このメソッドから、コントローラのアクションに適用するミドルウェアの配列を返します。
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Http\Controllers\Controller; 6use Illuminate\Routing\Controllers\HasMiddleware; 7use Illuminate\Routing\Controllers\Middleware; 8 9class UserController extends Controller implements HasMiddleware10{11 /**12 * Get the middleware that should be assigned to the controller.13 */14 public static function middleware(): array15 {16 return [17 'auth',18 new Middleware('log', only: ['index']),19 new Middleware('subscribed', except: ['store']),20 ];21 }22 23 // ...24}
コントローラのミドルウェアをクロージャとして定義することもできます。これにより、ミドルウェアクラス全体を記述せずにインラインミドルウェアを定義する便利な方法が提供されます。
1use Closure; 2use Illuminate\Http\Request; 3 4/** 5 * Get the middleware that should be assigned to the controller. 6 */ 7public static function middleware(): array 8{ 9 return [10 function (Request $request, Closure $next) {11 return $next($request);12 },13 ];14}
Illuminate\Routing\Controllers\HasMiddlewareを実装するコントローラは、Illuminate\Routing\Controllerを拡張してはいけません。
リソースコントローラ
アプリケーションの各Eloquentモデルを「リソース」と考えると、アプリケーションの各リソースに対して同じ一連のアクションを実行するのが一般的です。たとえば、アプリケーションにPhotoモデルとMovieモデルが含まれているとします。ユーザーはこれらのリソースを作成、読み取り、更新、または削除できる可能性が高いです。
この一般的なユースケースのため、Laravelのリソースルーティングは、一般的な作成、読み取り、更新、および削除(「CRUD」)ルートを1行のコードでコントローラに割り当てます。開始するには、make:controller Artisanコマンドの--resourceオプションを使用して、これらのアクションを処理するコントローラをすばやく作成します。
1php artisan make:controller PhotoController --resource
このコマンドは、app/Http/Controllers/PhotoController.phpにコントローラを生成します。コントローラには、利用可能な各リソース操作のメソッドが含まれます。次に、コントローラを指すリソースルートを登録できます。
1use App\Http\Controllers\PhotoController;2 3Route::resource('photos', PhotoController::class);
この単一のルート宣言は、リソース上のさまざまなアクションを処理するための複数のルートを作成します。生成されたコントローラには、これらの各アクションのメソッドがすでにスタブ化されています。route:list Artisanコマンドを実行することで、アプリケーションのルートの概要をいつでもすばやく確認できます。
resourcesメソッドに配列を渡すことで、一度に多くのリソースコントローラを登録することもできます。
1Route::resources([2 'photos' => PhotoController::class,3 'posts' => PostController::class,4]);
リソースコントローラによって処理されるアクション
| 動詞 | 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メソッドは、リソースのルートのいずれかで暗黙的にバインドされたモデルが見つからない場合に呼び出されるクロージャを受け入れます。
1use App\Http\Controllers\PhotoController;2use Illuminate\Http\Request;3use Illuminate\Support\Facades\Redirect;4 5Route::resource('photos', PhotoController::class)6 ->missing(function (Request $request) {7 return Redirect::route('photos.index');8 });
ソフトデリートされたモデル
通常、暗黙のモデルバインディングはソフトデリートされたモデルを取得せず、代わりに404 HTTPレスポンスを返します。ただし、リソースルートを定義するときにwithTrashedメソッドを呼び出すことで、フレームワークにソフトデリートされたモデルを許可するように指示できます。
1use App\Http\Controllers\PhotoController;2 3Route::resource('photos', PhotoController::class)->withTrashed();
引数なしでwithTrashedを呼び出すと、show、edit、updateリソースルートでソフトデリートされたモデルが許可されます。withTrashedメソッドに配列を渡すことで、これらのルートのサブセットを指定できます。
1Route::resource('photos', PhotoController::class)->withTrashed(['show']);
リソースモデルの指定
ルートモデルバインディングを使用していて、リソースコントローラのメソッドがモデルインスタンスをタイプヒントするようにしたい場合は、コントローラを生成するときに--modelオプションを使用できます。
1php artisan make:controller PhotoController --model=Photo --resource
フォームリクエストの生成
リソースコントローラを生成するときに--requestsオプションを指定して、Artisanにコントローラの保存および更新メソッド用のフォームリクエストクラスを生成するように指示できます。
1php artisan make:controller PhotoController --model=Photo --resource --requests
部分的なリソースルート
リソースルートを宣言するときに、デフォルトのアクションの完全なセットの代わりに、コントローラが処理する必要があるアクションのサブセットを指定できます。
1use App\Http\Controllers\PhotoController;2 3Route::resource('photos', PhotoController::class)->only([4 'index', 'show'5]);6 7Route::resource('photos', PhotoController::class)->except([8 'create', 'store', 'update', 'destroy'9]);
APIリソースルート
APIによって消費されるリソースルートを宣言する場合、通常はcreateやeditなどのHTMLテンプレートを提示するルートを除外したいでしょう。便宜上、apiResourceメソッドを使用して、これら2つのルートを自動的に除外できます。
1use App\Http\Controllers\PhotoController;2 3Route::apiResource('photos', PhotoController::class);
apiResourcesメソッドに配列を渡すことで、一度に多くのAPIリソースコントローラを登録できます。
1use App\Http\Controllers\PhotoController;2use App\Http\Controllers\PostController;3 4Route::apiResources([5 'photos' => PhotoController::class,6 'posts' => PostController::class,7]);
createまたはeditメソッドを含まないAPIリソースコントローラをすばやく生成するには、make:controllerコマンドを実行するときに--apiスイッチを使用します。
1php artisan make:controller PhotoController --api
ネストしたリソース
ネストされたリソースへのルートを定義する必要がある場合があります。たとえば、写真リソースには、写真に添付できる複数のコメントがある場合があります。リソースコントローラをネストするには、ルート宣言で「ドット」表記法を使用できます。
1use App\Http\Controllers\PhotoCommentController;2 3Route::resource('photos.comments', PhotoCommentController::class);
このルートは、次のようなURIでアクセスできるネストされたリソースを登録します。
1/photos/{photo}/comments/{comment}
ネストされたリソースのスコープ
Laravelの暗黙のモデルバインディング機能は、解決された子モデルが親モデルに属することを確認するようにネストされたバインディングを自動的にスコープできます。ネストされたリソースを定義するときにscopedメソッドを使用することで、自動スコープを有効にし、Laravelに子リソースを取得するフィールドを指示できます。これを実現する方法の詳細については、リソースルートのスコープに関するドキュメントを参照してください。
浅いネスト
多くの場合、子IDはすでに一意の識別子であるため、URI内に親IDと子IDの両方を含める必要は必ずしもありません。自動インクリメントする主キーなどの一意の識別子を使用してURIセグメント内のモデルを識別する場合、「浅いネスト」を使用することを選択できます。
1use App\Http\Controllers\CommentController;2 3Route::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配列を渡すことでこれらの名前を上書きできます。
1use App\Http\Controllers\PhotoController;2 3Route::resource('photos', PhotoController::class)->names([4 'create' => 'photos.build'5]);
リソースルートパラメータの命名
デフォルトでは、Route::resourceはリソース名の「単数形」バージョンに基づいてリソースルートのルートパラメータを作成します。parametersメソッドを使用して、リソースごとにこれを簡単にオーバーライドできます。parametersメソッドに渡される配列は、リソース名とパラメータ名の連想配列である必要があります。
1use App\Http\Controllers\AdminUserController;2 3Route::resource('users', AdminUserController::class)->parameters([4 'users' => 'admin_user'5]);
上記の例では、リソースのshowルートに対して次のURIが生成されます。
1/users/{admin_user}
リソースルートのスコープ
Laravelのスコープ付き暗黙のモデルバインディング機能は、解決された子モデルが親モデルに属することを確認するようにネストされたバインディングを自動的にスコープできます。ネストされたリソースを定義するときにscopedメソッドを使用することで、自動スコープを有効にし、Laravelに子リソースを取得するフィールドを指示できます。
1use App\Http\Controllers\PhotoCommentController;2 3Route::resource('photos.comments', PhotoCommentController::class)->scoped([4 'comment' => 'slug',5]);
このルートは、次のようなURIでアクセスできるスコープ付きネストリソースを登録します。
1/photos/{photo}/comments/{comment:slug}
ネストされたルートパラメータとしてカスタムキー付き暗黙的バインディングを使用する場合、Laravelは親の関係名を推測するための規則を使用して、その親によってネストされたモデルを取得するためにクエリを自動的にスコープします。この場合、Photoモデルには、Commentモデルを取得するために使用できるcomments(ルートパラメータ名の複数形)という名前の関係があると想定されます。
リソースURIのローカライズ
デフォルトでは、Route::resourceは英語の動詞と複数形ルールを使用してリソースURIを作成します。createとeditのアクション動詞をローカライズする必要がある場合は、Route::resourceVerbsメソッドを使用できます。これは、アプリケーションのApp\Providers\AppServiceProvider内のbootメソッドの先頭で行うことができます。
1/** 2 * Bootstrap any application services. 3 */ 4public function boot(): void 5{ 6 Route::resourceVerbs([ 7 'create' => 'crear', 8 'edit' => 'editar', 9 ]);10}
Laravelの複数形化機能は、ニーズに応じて設定できるいくつかの異なる言語をサポートしています。動詞と複数形化言語がカスタマイズされると、Route::resource('publicacion', PublicacionController::class)のようなリソースルート登録は次のURIを生成します。
1/publicacion/crear23/publicacion/{publicaciones}/editar
リソースコントローラの補足
デフォルトのリソースルートのセットに加えて、リソースコントローラにさらにルートを追加する必要がある場合は、Route::resourceメソッドの呼び出しの前にそれらのルートを定義する必要があります。そうしないと、resourceメソッドによって定義されたルートが意図せずに補足ルートより優先される可能性があります。
1use App\Http\Controller\PhotoController;2 3Route::get('/photos/popular', [PhotoController::class, 'popular']);4Route::resource('photos', PhotoController::class);
コントローラを集中させておくことを忘れないでください。典型的なリソースアクションのセット以外のメソッドが定期的に必要になる場合は、コントローラを2つの小さなコントローラに分割することを検討してください。
シングルトンリソースコントローラ
アプリケーションには、単一のインスタンスしか持てないリソースが存在する場合があります。たとえば、ユーザーの「プロフィール」は編集または更新できますが、ユーザーは複数の「プロフィール」を持つことはできません。同様に、画像には単一の「サムネイル」しかありません。これらのリソースは「シングルトンリソース」と呼ばれ、リソースのインスタンスが1つだけ存在することを意味します。このようなシナリオでは、「シングルトン」リソースコントローラを登録できます。
1use App\Http\Controllers\ProfileController;2use Illuminate\Support\Facades\Route;3 4Route::singleton('profile', ProfileController::class);
上記のシングルトンリソース定義は、次のルートを登録します。ご覧のとおり、シングルトンリソースには「作成」ルートは登録されず、登録されたルートはリソースのインスタンスが1つしか存在しないため識別子を受け入れません。
| 動詞 | URI | アクション | ルート名 |
|---|---|---|---|
| GET | /profile |
show | profile.show |
| GET | /profile/edit |
edit | profile.edit |
| PUT/PATCH | /profile |
update | profile.update |
シングルトンリソースは、標準リソース内にネストすることもできます。
1Route::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メソッドを呼び出します。
1Route::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メソッドを利用できます。
1Route::singleton(...)->destroyable();
APIシングルトンリソース
apiSingletonメソッドを使用して、API経由で操作されるシングルトンリソースを登録できます。これにより、createルートとeditルートが不要になります。
1Route::apiSingleton('profile', ProfileController::class);
もちろん、APIシングルトンリソースもcreatableにすることができ、これによりリソースのstoreルートとdestroyルートが登録されます。
1Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
依存注入とコントローラ
コンストラクタインジェクション
Laravelのサービスコンテナは、すべてのLaravelコントローラを解決するために使用されます。その結果、コントローラが必要とする依存関係をコンストラクタでタイプヒントできます。宣言された依存関係は自動的に解決され、コントローラインスタンスに注入されます。
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Repositories\UserRepository; 6 7class UserController extends Controller 8{ 9 /**10 * Create a new controller instance.11 */12 public function __construct(13 protected UserRepository $users,14 ) {}15}
メソッドインジェクション
コンストラクタインジェクションに加えて、コントローラのメソッドで依存関係をタイプヒントすることもできます。メソッドインジェクションの一般的なユースケースは、Illuminate\Http\Requestインスタンスをコントローラメソッドに注入することです。
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\RedirectResponse; 6use Illuminate\Http\Request; 7 8class UserController extends Controller 9{10 /**11 * Store a new user.12 */13 public function store(Request $request): RedirectResponse14 {15 $name = $request->name;16 17 // Store the user...18 19 return redirect('/users');20 }21}
コントローラメソッドがルートパラメータからの入力も期待している場合は、他の依存関係の後にルート引数をリストします。たとえば、ルートが次のように定義されている場合:
1use App\Http\Controllers\UserController;2 3Route::put('/user/{id}', [UserController::class, 'update']);
次のようにコントローラメソッドを定義することで、Illuminate\Http\Requestをタイプヒントし、idパラメータにアクセスできます。
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\RedirectResponse; 6use Illuminate\Http\Request; 7 8class UserController extends Controller 9{10 /**11 * Update the given user.12 */13 public function update(Request $request, string $id): RedirectResponse14 {15 // Update the user...16 17 return redirect('/users');18 }19}