#はじめに
ルートファイル内でリクエスト処理のロジックをすべてクロージャとして定義する代わりに、「コントローラー」クラスを使って処理を整理できます。コントローラーは関連するリクエスト処理を1つのクラスにまとめられます。例えば、UserController クラスはユーザーに関するすべてのリクエスト(表示、作成、更新、削除など)を処理します。コントローラーはデフォルトで app/Http/Controllers ディレクトリに保存されます。
#コントローラーの作成
#基本的なコントローラー
新しいコントローラーを素早く生成するには、make:controller Artisanコマンドを実行します。アプリケーションのすべてのコントローラーはデフォルトで 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にリクエストがマッチすると、App\Http\Controllers\UserController クラスの show メソッドが呼び出され、ルートパラメータがメソッドに渡されます。
コントローラーはベースクラスを継承する必要はありません。ただし、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);
make:controller Artisanコマンドの --invokable オプションを使うと、シングルアクションコントローラーを生成できます:
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(作成、読み取り、更新、削除)ルートを1行のコードでコントローラーに割り当てます。まずは make:controller Artisanコマンドの --resource オプションを使って、これらの操作を処理するコントローラーを素早く作成しましょう:
php artisan make:controller PhotoController --resource
このコマンドは app/Http/Controllers/PhotoController.php にコントローラーを生成します。コントローラーには利用可能なリソース操作ごとのメソッドが含まれます。次に、コントローラーを指すリソースルートを登録します:
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');
});
#ソフトデリートされたモデル
通常、暗黙的モデルバインディングはソフトデリートされたモデルを取得せず、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 オプションを指定すると、ストアとアップデートメソッド用のフォームリクエストクラスが生成されます:
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で利用するリソースルートを宣言する場合、create や edit のようなHTMLテンプレートを返すルートを除外したいことが多いです。便利な apiResource メソッドを使うと、この2つのルートを自動的に除外できます:
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,
]);
make:controller コマンドで --api オプションを使うと、create と edit メソッドを含まないAPIリソースコントローラーを素早く生成できます:
php artisan make:controller PhotoController --api
#ネストされたリソース
ネストされたリソースへのルートを定義する必要がある場合があります。例えば、写真リソースに複数のコメントが付く場合です。リソースコントローラーをネストするには、ルート宣言で「ドット」表記を使います:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
このルートは次のようなURIでアクセスできるネストされたリソースを登録します:
/photos/{photo}/comments/{comment}
#ネストリソースのスコープ設定
Laravelの暗黙的モデルバインディング機能は、解決された子モデルが親モデルに属していることを自動的にスコープ設定します。ネストされたリソースを定義する際に scoped メソッドを使うと、自動スコープ設定を有効にし、子リソースを取得するフィールドを指定できます。詳細はリソースルートのスコープ設定のドキュメントを参照してください。
#シャローネスティング
URIに親と子の両方のIDを含める必要は必ずしもありません。子IDがすでに一意の識別子である場合です。自動インクリメントの主キーなど一意の識別子を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'
]);
上記の例では、リソースの show ルートに対して以下のURIが生成されます:
/users/{admin_user}
#リソースルートのスコープ設定
Laravelのスコープ付き暗黙的モデルバインディング機能は、ネストされたバインディングを自動的にスコープし、子モデルが親モデルに属していることを確認します。ネストされたリソースを定義する際に scoped メソッドを使うと、自動スコープを有効にし、子リソースを取得するフィールドを指定できます。
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 メソッドを使えます。これはアプリケーションの App\Providers\RouteServiceProvider の boot メソッドの冒頭で設定します。
/**
* ルートモデルバインディングやパターンフィルターなどを定義します。
*/
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);
コントローラーは役割を絞って設計しましょう。もし通常のリソースアクション以外のメソッドが頻繁に必要になる場合は、コントローラーを2つの小さなコントローラーに分割することを検討してください。
#シングルトンリソースコントローラー
アプリケーションによっては、単一のインスタンスしか存在しないリソースがあります。例えば、ユーザーの「プロフィール」は編集や更新ができますが、複数のプロフィールは持てません。同様に、画像には1つの「サムネイル」があります。これらは「シングルトンリソース」と呼ばれ、1つだけ存在するリソースです。このような場合、シングルトンリソースコントローラーを登録できます。
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::singleton('profile', ProfileController::class);
上記のシングルトンリソース定義は以下のルートを登録します。ご覧の通り、シングルトンリソースには「作成」ルートが登録されず、リソースが1つしか存在しないため識別子を受け付けません。
| 動詞 | 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 |
シングルトンリソースに対して 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のサービスコンテナはすべてのコントローラーを解決します。そのため、コントローラーのコンストラクタで必要な依存性を型宣言すれば、自動的に解決されて注入されます。
<?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');
}
}