コントローラー
リクエストを受けて何を返すかを決めるコントローラーの作り方を説明します。1つの動きだけのもの・リソースコントローラー・ミドルウェアの付け方・部品の受け取り方まで引けます。
コントローラーは、リクエスト(ブラウザからのお願い)を受けて、何を返すかを決めるクラス(処理をまとめた入れ物)です。お店でいえば、受付係(ルート)に案内されたお客さんの用件を、実際に片づける担当者です。
ルートのファイルに、処理をクロージャ(名前のない関数)で全部書くこともできます。でも数が増えると読みにくくなります。そこで、関係する処理を1つのクラスにまとめます。たとえば UserController なら、ユーザーを見せる・作る・直す・消す処理をまとめて持てます。コントローラーは、ふつう app/Http/Controllers フォルダに置きます。
コントローラーを書く#
基本のコントローラー#
make:controller という Artisan コマンド(php artisan で動かす Laravel のコマンド)で、ひな形が作れます。
php artisan make:controller UserController
コントローラーは、リクエストに答える public な(外から呼べる)メソッド(クラスの中の関数)をいくつでも持てます。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for a given user.
*/
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']);
リクエストの URL がルートに合うと、App\Http\Controllers\UserController の show メソッドが動き、ルートパラメータ(URL の {id} の部分)が引数として渡されます。
補足
コントローラーは、ほかのクラスを継承(元のクラスの機能を引き継ぐこと)しなくてもかまいません。ただ、すべてのコントローラーで共通して使いたいメソッドがあるとき、土台になるクラスを継承すると便利です。
1つの動きだけのコントローラー#
1つの処理がとても複雑なときは、その処理だけのためにクラスを1つ作ると便利です。クラスには __invoke というメソッドを1つだけ書きます。
<?php
namespace App\Http\Controllers;
class ProvisionServer extends Controller
{
/**
* Provision a new web server.
*/
public function __invoke()
{
// ...
}
}
ルートには、メソッド名を書かず、クラス名だけ渡します。
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
make:controller に --invokable を付けると、このひな形が作れます。
php artisan make:controller ProvisionServer --invokable
補足
コントローラーのひな形は、ひな形の取り出し(stub publishing)で変えられます。くわしくは Artisan コマンドを見てください。
コントローラーにミドルウェアを付ける#
ミドルウェア(リクエストが届く前に間に入って確かめる処理)は、ルートのファイルで付けられます。
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');
コントローラーのクラスの中で決めることもできます。HasMiddleware というインターフェイス(守るべきメソッドの決まり)をクラスに付け(implements)、static な(クラスから直接呼べる)middleware メソッドを書きます。そこから、使うミドルウェアの配列を返します。
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class UserController implements HasMiddleware
{
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return [
'auth',
new Middleware('log', only: ['index']),
new Middleware('subscribed', except: ['store']),
];
}
// ...
}
only は、そのメソッドだけにかけます。except は、そのメソッド以外にかけます。
クロージャでミドルウェアを書くこともできます。ミドルウェアのクラスを作らずに、その場で書ける近道です。
use Closure;
use Illuminate\Http\Request;
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return [
function (Request $request, Closure $next) {
return $next($request);
},
];
}
属性でミドルウェアを付ける#
PHP の属性(クラスやメソッドの前に書く印)でも、ミドルウェアを付けられます。
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
// ...
}
メソッドごとにも付けられます。メソッドに付けたものは、クラスに付けたものと合わさります。
<?php
namespace App\Http\Controllers;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
class UserController
{
#[Middleware('log')]
#[Middleware('subscribed')]
public function index()
{
// ...
}
#[Middleware(static function (Request $request, Closure $next) {
// ...
return $next($request);
})]
public function store()
{
// ...
}
}
ミドルウェアを外したいときは、WithoutMiddleware の属性を使います。クラスに付けた属性は、only と except で、特定のメソッドだけに絞れます。
<?php
namespace App\Http\Controllers;
use App\Http\Middleware\EnsureTokenIsValid;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;
#[WithoutMiddleware('subscribed', except: ['index'])]
class UserController
{
#[WithoutMiddleware(EnsureTokenIsValid::class)]
public function index()
{
// ...
}
public function show()
{
// ...
}
}
クラスに付けた WithoutMiddleware は、子のコントローラーにも引き継がれます。外せるのはルートのミドルウェアだけで、全体にかかるミドルウェアは外せません。
属性で権限を確かめる#
コントローラーのメソッドを動かしてよいかを、ポリシー(「この人はこれをしてよいか」を決めるクラス)で確かめるなら、Authorize の属性が使えます。これは can ミドルウェアの近道です。
<?php
namespace App\Http\Controllers;
use App\Models\Comment;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;
class CommentController
{
#[Authorize('create', [Comment::class, 'post'])]
public function store(Post $post)
{
// ...
}
#[Authorize('delete', 'comment')]
public function destroy(Comment $comment)
{
// ...
}
}
1つ目の引数は、確かめたい操作の名前です。2つ目は、ポリシーに渡す、モデルのクラスかルートパラメータの名前(複数なら配列)です。
リソースコントローラー#
アプリの Eloquent モデル(データベースの表を PHP から扱いやすくしたクラス)を、それぞれ「リソース」(アプリが扱うデータのまとまり)と考えます。写真(Photo)や映画(Movie)のどれにも、作る・見る・直す・消すという同じ操作をしたくなるのがふつうです。
この定番の操作を、1行で全部ルートにする機能が、リソースルートです。まず、make:controller に --resource を付けて、コントローラーを作ります。
php artisan make:controller PhotoController --resource
app/Http/Controllers/PhotoController.php ができます。使える操作ごとに、メソッドが1つずつ入っています。次に、リソースルートを登録します。
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);
この1行で、いろいろな操作のルートがまとめてできます。ルートの様子は route:list コマンドで見られます。
配列で渡せば、いくつものリソースコントローラーを一度に登録できます。
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
softDeletableResources は、複数のリソースを登録し、どれにも withTrashed(論理削除したモデルも取り出せるようにする設定。下で説明します)を付けます。
Route::softDeletableResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
リソースコントローラーが受け持つ操作#
| メソッドと URL | 説明 |
|---|---|
GET /photos(index、名前 photos.index) |
一覧を見せる |
GET /photos/create(create、名前 photos.create) |
新しく作る画面を見せる |
POST /photos(store、名前 photos.store) |
新しく作って保存する |
GET /photos/{photo}(show、名前 photos.show) |
1件を見せる |
GET /photos/{photo}/edit(edit、名前 photos.edit) |
直す画面を見せる |
PUT/PATCH /photos/{photo}(update、名前 photos.update) |
直して保存する |
DELETE /photos/{photo}(destroy、名前 photos.destroy) |
消す |
モデルが見つからないときの動き#
ルートモデルバインディング(ルートの値から、モデルを自動で探す機能。くわしくはルーティング)でモデルが見つからないと、ふつうは 404 が返ります。リソースルートに 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 になります。取り出したいときは、リソースルートに withTrashed を付けます。
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->withTrashed();
引数なしで呼ぶと、show・edit・update のルートで取り出せるようになります。配列で渡せば、一部のルートだけにできます。
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
make:controller で使ったオプションをまとめます。
| オプション | 説明 |
|---|---|
--invokable |
1つの動きだけのコントローラーを作る |
--resource |
リソースコントローラーを作る |
--model=Photo |
メソッドに、そのモデルの型を書く |
--requests |
保存と更新用のフォームリクエストも作る |
--api |
create と edit を持たない、API 用のリソースコントローラーを作る |
一部の操作だけにする#
リソースルートで、使う操作を絞れます。
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
Route::resource('photos', PhotoController::class)->except([
'create', 'store', 'update', 'destroy'
]);
only は、挙げた操作だけを作ります。except は、挙げた操作以外を作ります。
API 用のリソースルート#
API(ほかのプログラムが使う窓口)向けのときは、HTML の画面を見せる create と edit が要りません。apiResource を使うと、この2つを自動で外せます。
use App\Http\Controllers\PhotoController;
Route::apiResource('photos', PhotoController::class);
apiResources に配列で渡せば、いくつも一度に登録できます。
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
create と edit を持たないコントローラーは、--api で作れます。
php artisan make:controller PhotoController --api
入れ子のリソース#
リソースの中に、別のリソースがあることもあります。たとえば、写真にコメントが付くときです。ルートの書き方で、ドット(.)でつなぐと、入れ子になります。
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
次のような URL で使えます。
/photos/{photo}/comments/{comment}
入れ子の中を親で絞る#
Laravel の暗黙のバインディング(ルートモデルバインディングの、自動で結びつける形)には、入れ子の子のモデルが親のものかを確かめる機能があります。入れ子のリソースに scoped を付けると、この確認が有効になり、子を探す列も決められます。くわしくは、下の「リソースルートを親で絞る」を見てください。
浅い入れ子#
子の ID だけで、ほかとかぶらずに探せるなら、URL に親の ID まで入れなくてもよいことがあります。自動で増える番号のように、重ならない ID を使うなら、浅い入れ子(shallow)が使えます。
use App\Http\Controllers\CommentController;
Route::resource('photos.comments', CommentController::class)->shallow();
次のルートができます。
| メソッドと URL | 説明 |
|---|---|
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) |
1件のコメントを見せる |
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 メソッドで、リソースごとに変えられます。リソース名と、パラメータ名の連想配列(名前と値の組の並び)を渡します。
use App\Http\Controllers\AdminUserController;
Route::resource('users', AdminUserController::class)->parameters([
'users' => 'admin_user'
]);
この例では、show のルートが次の URL になります。
/users/{admin_user}
リソースルートを親で絞る#
ルーティングで説明した、親の中から子を探す機能(スコープ付きのバインディング)は、入れ子のリソースにも使えます。scoped メソッドで有効にし、子を探す列も決められます。
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);
次のような URL で使えます。
/photos/{photo}/comments/{comment:slug}
入れ子のルートパラメータに、id 以外の列を決めると、Laravel は、親から子を探すようにします。このとき、親のモデルに、パラメータ名の複数形のリレーション(表どうしのつながり)がある、と見なします。この例なら、Photo に comments というリレーションがあるはずです。
リソースの URL を翻訳する#
Route::resource は、英語の動詞と複数形の決まりで URL を作ります。create と edit の言葉を変えたいときは、Route::resourceVerbs を使います。App\Providers\AppServiceProvider の boot メソッドの最初に書きます。
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
}
Laravel の複数形にする機能は、いろいろな言語に対応しています。言語の設定は、多言語対応で説明しています。動詞と言語を変えたあとで、Route::resource('publicacion', PublicacionController::class) を登録すると、次の URL ができます。
/publicacion/crear
/publicacion/{publicacion}/editar
リソースコントローラーにルートを足す#
リソースの定番のルートのほかに、ルートを足したいときは、Route::resource を呼ぶ前に書きます。後ろに書くと、リソースのルートが先に当たってしまうことがあります。
use App\Http\Controllers\PhotoController;
Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);
補足
コントローラーには、仕事を詰めこみすぎないようにします。定番の操作のほかにメソッドを足すことがよくあるなら、小さなコントローラー2つに分けることを考えましょう。
1つしかないリソース(シングルトン)#
リソースには、1つしか持てないものがあります。たとえば、ユーザーの「プロフィール」は、直せますが、2つは持てません。画像の「サムネイル」も1つです。このようなものを「シングルトンのリソース」と呼びます。シングルトンのコントローラーは、次のように登録します。
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::singleton('profile', ProfileController::class);
次のルートができます。作る操作はなく、また、リソースが1つだけなので、ID も受け取りません。
| メソッドと URL | 説明 |
|---|---|
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 はシングルトンとして、次のルートになります。
| メソッドと URL | 説明 |
|---|---|
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 のルートも入ります。
| メソッドと URL | 説明 |
|---|---|
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();
リソースコントローラーとミドルウェア#
リソースのルートには、すべてか、一部の操作だけに、ミドルウェアを付けられます。middleware・middlewareFor・withoutMiddlewareFor を使い、操作ごとに細かく決められます。
| メソッド | 説明 |
|---|---|
middleware |
リソースのすべてのルートに付ける |
middlewareFor |
指定した操作のルートだけに付ける |
withoutMiddlewareFor |
指定した操作のルートから外す |
すべての操作に付ける#
リソースかシングルトンが作るルートすべてに付けるときは、middleware を使います。
Route::resource('users', UserController::class)
->middleware(['auth', 'verified']);
Route::singleton('profile', ProfileController::class)
->middleware('auth');
特定の操作だけに付ける#
リソースコントローラーの、1つ以上の操作だけに付けるときは、middlewareFor を使います。
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 は、シングルトンと API 用のシングルトンにも使えます。
Route::singleton('profile', ProfileController::class)
->middlewareFor('show', 'auth');
Route::apiSingleton('profile', ProfileController::class)
->middlewareFor(['show', 'update'], 'auth');
特定の操作から外す#
リソースコントローラーの、特定の操作から外すときは、withoutMiddlewareFor を使います。
Route::middleware(['auth', 'verified', 'subscribed'])->group(function () {
Route::resource('users', UserController::class)
->withoutMiddlewareFor('index', ['auth', 'verified'])
->withoutMiddlewareFor(['create', 'store'], 'verified')
->withoutMiddlewareFor('destroy', 'subscribed');
});
必要なものを受け取る(依存性の注入)#
コンストラクタで受け取る#
コントローラーは、すべてサービスコンテナ(クラスを作って渡してくれる道具箱)から作られます。そのため、コンストラクタ(クラスを作るときに動く部分)に型を書けば、必要な部品が自動で渡されます。これを依存性の注入といいます。
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Create a new controller instance.
*/
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
{
/**
* Store a new user.
*/
public function store(Request $request): RedirectResponse
{
$name = $request->name;
// ユーザーを保存する
return redirect('/users');
}
}
ルートパラメータも受け取りたいときは、ほかの部品の後ろに並べます。たとえば、次のルートがあるとします。
use App\Http\Controllers\UserController;
Route::put('/user/{id}', [UserController::class, 'update']);
Request の型を書きながら、id も受け取るには、次のように書きます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Update the given user.
*/
public function update(Request $request, string $id): RedirectResponse
{
// ユーザーを更新する
return redirect('/users');
}
}
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。