本文へ移動
Laravel Tips

コントローラー

リクエストを受けて何を返すかを決めるコントローラーの作り方を説明します。1つの動きだけのもの・リソースコントローラー・ミドルウェアの付け方・部品の受け取り方まで引けます。

コントローラーは、リクエスト(ブラウザからのお願い)を受けて、何を返すかを決めるクラス(処理をまとめた入れ物)です。お店でいえば、受付係(ルート)に案内されたお客さんの用件を、実際に片づける担当者です。

ルートのファイルに、処理をクロージャ(名前のない関数)で全部書くこともできます。でも数が増えると読みにくくなります。そこで、関係する処理を1つのクラスにまとめます。たとえば UserController なら、ユーザーを見せる・作る・直す・消す処理をまとめて持てます。コントローラーは、ふつう app/Http/Controllers フォルダに置きます。

コントローラーを書く#

基本のコントローラー#

make:controller という Artisan コマンド(php artisan で動かす Laravel のコマンド)で、ひな形が作れます。

bash
php artisan make:controller UserController

コントローラーは、リクエストに答える public な(外から呼べる)メソッド(クラスの中の関数)をいくつでも持てます。

php
<?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)
        ]);
    }
}

書いたら、ルートからそのメソッドを呼ぶように決めます。

php
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
<?php

namespace App\Http\Controllers;

class ProvisionServer extends Controller
{
    /**
     * Provision a new web server.
     */
    public function __invoke()
    {
        // ...
    }
}

ルートには、メソッド名を書かず、クラス名だけ渡します。

php
use App\Http\Controllers\ProvisionServer;

Route::post('/server', ProvisionServer::class);

make:controller に --invokable を付けると、このひな形が作れます。

bash
php artisan make:controller ProvisionServer --invokable

補足

コントローラーのひな形は、ひな形の取り出し(stub publishing)で変えられます。くわしくは Artisan コマンドを見てください。

コントローラーにミドルウェアを付ける#

ミドルウェア(リクエストが届く前に間に入って確かめる処理)は、ルートのファイルで付けられます。

php
Route::get('/profile', [UserController::class, 'show'])->middleware('auth');

コントローラーのクラスの中で決めることもできます。HasMiddleware というインターフェイス(守るべきメソッドの決まり)をクラスに付け(implements)、static な(クラスから直接呼べる)middleware メソッドを書きます。そこから、使うミドルウェアの配列を返します。

php
<?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 は、そのメソッド以外にかけます。

クロージャでミドルウェアを書くこともできます。ミドルウェアのクラスを作らずに、その場で書ける近道です。

php
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
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // ...
}

メソッドごとにも付けられます。メソッドに付けたものは、クラスに付けたものと合わさります。

php
<?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
<?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
<?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 を付けて、コントローラーを作ります。

bash
php artisan make:controller PhotoController --resource

app/Http/Controllers/PhotoController.php ができます。使える操作ごとに、メソッドが1つずつ入っています。次に、リソースルートを登録します。

php
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class);

この1行で、いろいろな操作のルートがまとめてできます。ルートの様子は route:list コマンドで見られます。

配列で渡せば、いくつものリソースコントローラーを一度に登録できます。

php
Route::resources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);

softDeletableResources は、複数のリソースを登録し、どれにも withTrashed(論理削除したモデルも取り出せるようにする設定。下で説明します)を付けます。

php
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 を付けると、そのときの動きを変えられます。クロージャは、リソースのどのルートでも、見つからなかったときに動きます。

php
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 を付けます。

php
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class)->withTrashed();

引数なしで呼ぶと、show・edit・update のルートで取り出せるようになります。配列で渡せば、一部のルートだけにできます。

php
Route::resource('photos', PhotoController::class)->withTrashed(['show']);

リソースのモデルを決める#

ルートモデルバインディングを使い、コントローラーのメソッドにモデルの型を書きたいときは、作るときに --model を付けます。

bash
php artisan make:controller PhotoController --model=Photo --resource

フォームリクエストも作る#

--requests を付けると、保存と更新のメソッド用に、フォームリクエストのクラス(入力のチェックを書くクラス。くわしくはバリデーション)も作れます。

bash
php artisan make:controller PhotoController --model=Photo --resource --requests

make:controller で使ったオプションをまとめます。

オプション 説明
--invokable 1つの動きだけのコントローラーを作る
--resource リソースコントローラーを作る
--model=Photo メソッドに、そのモデルの型を書く
--requests 保存と更新用のフォームリクエストも作る
--api create と edit を持たない、API 用のリソースコントローラーを作る

一部の操作だけにする#

リソースルートで、使う操作を絞れます。

php
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つを自動で外せます。

php
use App\Http\Controllers\PhotoController;

Route::apiResource('photos', PhotoController::class);

apiResources に配列で渡せば、いくつも一度に登録できます。

php
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;

Route::apiResources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);

create と edit を持たないコントローラーは、--api で作れます。

bash
php artisan make:controller PhotoController --api

入れ子のリソース#

リソースの中に、別のリソースがあることもあります。たとえば、写真にコメントが付くときです。ルートの書き方で、ドット(.)でつなぐと、入れ子になります。

php
use App\Http\Controllers\PhotoCommentController;

Route::resource('photos.comments', PhotoCommentController::class);

次のような URL で使えます。

text
/photos/{photo}/comments/{comment}

入れ子の中を親で絞る#

Laravel の暗黙のバインディング(ルートモデルバインディングの、自動で結びつける形)には、入れ子の子のモデルが親のものかを確かめる機能があります。入れ子のリソースに scoped を付けると、この確認が有効になり、子を探す列も決められます。くわしくは、下の「リソースルートを親で絞る」を見てください。

浅い入れ子#

子の ID だけで、ほかとかぶらずに探せるなら、URL に親の ID まで入れなくてもよいことがあります。自動で増える番号のように、重ならない ID を使うなら、浅い入れ子(shallow)が使えます。

php
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 の配列で、名前を変えられます。

php
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class)->names([
    'create' => 'photos.build'
]);

リソースルートのパラメータの名前を決める#

Route::resource は、リソース名を単数形にした名前で、ルートパラメータを作ります。parameters メソッドで、リソースごとに変えられます。リソース名と、パラメータ名の連想配列(名前と値の組の並び)を渡します。

php
use App\Http\Controllers\AdminUserController;

Route::resource('users', AdminUserController::class)->parameters([
    'users' => 'admin_user'
]);

この例では、show のルートが次の URL になります。

text
/users/{admin_user}

リソースルートを親で絞る#

ルーティングで説明した、親の中から子を探す機能(スコープ付きのバインディング)は、入れ子のリソースにも使えます。scoped メソッドで有効にし、子を探す列も決められます。

php
use App\Http\Controllers\PhotoCommentController;

Route::resource('photos.comments', PhotoCommentController::class)->scoped([
    'comment' => 'slug',
]);

次のような URL で使えます。

text
/photos/{photo}/comments/{comment:slug}

入れ子のルートパラメータに、id 以外の列を決めると、Laravel は、親から子を探すようにします。このとき、親のモデルに、パラメータ名の複数形のリレーション(表どうしのつながり)がある、と見なします。この例なら、Photo に comments というリレーションがあるはずです。

リソースの URL を翻訳する#

Route::resource は、英語の動詞と複数形の決まりで URL を作ります。create と edit の言葉を変えたいときは、Route::resourceVerbs を使います。App\Providers\AppServiceProvider の boot メソッドの最初に書きます。

php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
    ]);
}

Laravel の複数形にする機能は、いろいろな言語に対応しています。言語の設定は、多言語対応で説明しています。動詞と言語を変えたあとで、Route::resource('publicacion', PublicacionController::class) を登録すると、次の URL ができます。

text
/publicacion/crear

/publicacion/{publicacion}/editar

リソースコントローラーにルートを足す#

リソースの定番のルートのほかに、ルートを足したいときは、Route::resource を呼ぶ前に書きます。後ろに書くと、リソースのルートが先に当たってしまうことがあります。

php
use App\Http\Controllers\PhotoController;

Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);

補足

コントローラーには、仕事を詰めこみすぎないようにします。定番の操作のほかにメソッドを足すことがよくあるなら、小さなコントローラー2つに分けることを考えましょう。

1つしかないリソース(シングルトン)#

リソースには、1つしか持てないものがあります。たとえば、ユーザーの「プロフィール」は、直せますが、2つは持てません。画像の「サムネイル」も1つです。このようなものを「シングルトンのリソース」と呼びます。シングルトンのコントローラーは、次のように登録します。

php
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) 更新する

シングルトンは、ふつうのリソースの中に入れ子にもできます。

php
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 を呼びます。

php
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 を使います。

php
Route::singleton(...)->destroyable();

API 用のシングルトン#

apiSingleton は、API で扱うシングルトンを登録します。create と edit のルートは作りません。

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

API 用のシングルトンも creatable にできます。store と destroy のルートが入ります。

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

リソースコントローラーとミドルウェア#

リソースのルートには、すべてか、一部の操作だけに、ミドルウェアを付けられます。middleware・middlewareFor・withoutMiddlewareFor を使い、操作ごとに細かく決められます。

メソッド 説明
middleware リソースのすべてのルートに付ける
middlewareFor 指定した操作のルートだけに付ける
withoutMiddlewareFor 指定した操作のルートから外す

すべての操作に付ける#

リソースかシングルトンが作るルートすべてに付けるときは、middleware を使います。

php
Route::resource('users', UserController::class)
    ->middleware(['auth', 'verified']);

Route::singleton('profile', ProfileController::class)
    ->middleware('auth');

特定の操作だけに付ける#

リソースコントローラーの、1つ以上の操作だけに付けるときは、middlewareFor を使います。

php
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 用のシングルトンにも使えます。

php
Route::singleton('profile', ProfileController::class)
    ->middlewareFor('show', 'auth');

Route::apiSingleton('profile', ProfileController::class)
    ->middlewareFor(['show', 'update'], 'auth');

特定の操作から外す#

リソースコントローラーの、特定の操作から外すときは、withoutMiddlewareFor を使います。

php
Route::middleware(['auth', 'verified', 'subscribed'])->group(function () {
    Route::resource('users', UserController::class)
        ->withoutMiddlewareFor('index', ['auth', 'verified'])
        ->withoutMiddlewareFor(['create', 'store'], 'verified')
        ->withoutMiddlewareFor('destroy', 'subscribed');
});

必要なものを受け取る(依存性の注入)#

コンストラクタで受け取る#

コントローラーは、すべてサービスコンテナ(クラスを作って渡してくれる道具箱)から作られます。そのため、コンストラクタ(クラスを作るときに動く部分)に型を書けば、必要な部品が自動で渡されます。これを依存性の注入といいます。

php
<?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
<?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');
    }
}

ルートパラメータも受け取りたいときは、ほかの部品の後ろに並べます。たとえば、次のルートがあるとします。

php
use App\Http\Controllers\UserController;

Route::put('/user/{id}', [UserController::class, 'update']);

Request の型を書きながら、id も受け取るには、次のように書きます。

php
<?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日時点の内容をもとに、日本語でまとめています。

ページの一覧