Laravelのコントローラー種類まとめ — 保守しやすい構成の選定ポイント

  • URLをコピーしました!

Laravelで開発を続けていると、「気づけばコントローラーが長大化して読みにくい…」という悩みにぶつかることがあります。

特にCRUD機能を詰め込んだUserControllerなどは、あとから見直すと迷路のようになりがちです。

この記事では、そんな“Fat Controller”問題を回避するための設計視点として、Laravelが用意する3種類のコントローラー形式の使い分け方を整理しました。

業務での実例や判断基準も交えながら、実装と保守を見据えた選択ができるように解説します。

【この記事を読むとわかること】

  • Fat Controllerを避けるためのコントローラー分割の考え方
  • 通常型・resource型・invokable型コントローラーの特徴と使いどころ
  • 実務で迷わないための判断軸と実装上の工夫ポイント
目次

Laravelのコントローラー設計を考える前に

Laravelを触り始めてしばらくすると、「コントローラーが膨らみすぎて見通しが悪い」と感じる瞬間が訪れます。とくにCRUD処理を積み上げていくと、1クラスに処理が詰まりがちです。

その結果、あとから読んだ人にとって「何がどこにあるのか」が分かりづらくなる。これが俗にいう「Fat Controller問題」です。

この問題を避けるには、「種類ごとに適切なコントローラーを使い分ける」設計の視点が重要です。Laravelでは複数の書き方が用意されているため、目的に応じた選択が必要になります。

ややこしそうに思えても、基本的な考え方を押さえれば整理はそれほど難しくありません。

よくある「Fat Controller」問題とは何か

業務でフォーム登録や一覧取得などを一つのクラスに集約していると、だんだんコントローラーが長文化しがちです。

たとえば UserController に登録・更新・一覧・削除…と書き足していくと、結果的にロジックが埋もれた状態になってしまいます。

これは一見「まとめてあって分かりやすい」と思えるのですが、実際は責務が曖昧になっており、保守や拡張の足を引っ張ります。特にバリデーションやDB操作を直接書いていると、さらに見通しが悪くなります。

コントローラーの種類を正しく選ぶことで、処理の粒度を揃えたり、ロジックの移譲先を明確にすることができるようになります。最初は小さな工夫でも、後々のコードの読みやすさが大きく変わってきます。

なぜ種類を使い分ける必要があるのか

Laravelでは、シンプルなアクションを1メソッドで書く方法から、RESTfulな構成に最適化されたresource型、さらに単一のアクション専用のシングルアクションコントローラーまで、複数の型が用意されています。

どれもLaravelの思想に沿った使い方が前提となっており、「どの型を選ぶか」でプロジェクト全体の統一感や保守性が変わってきます。

言い換えれば、「どう設計するか」は技術選定というより、チーム開発の共通言語を整えることにもつながります。


Laravelのコントローラーの種類と違い

Laravelでは大きく分けて3種類のコントローラーが使われます。それぞれ構造や記述方法が異なるため、まずは概要を整理しておくのが大切です。

「どう書くか」だけでなく、「何のためにこの形式があるのか」を意識しておくと、選びやすくなります。

種類主な特徴使用例artisanコマンド例
通常のコントローラー複数メソッドを自由に定義できる管理画面など、画面遷移と処理が混在する場面php artisan make:controller UserController
リソースコントローラーRESTful構造に対応、7つの定義済みメソッドCRUD系のAPIやユーザー管理系php artisan make:controller UserController --resource
シングルアクションコントローラー単一の__invokeメソッドのみ簡易なAPI、通知、登録など小粒な処理php artisan make:controller SendNotification --invokable

通常のコントローラーとresourceの違い

通常のコントローラーは自由度が高く、任意のメソッド名で処理を定義できます。一方、resourceコントローラーはRESTfulなアクション構成(index, store, updateなど)を前提にしています。

// 通常のコントローラー(自由に定義)
class UserController extends Controller
{
    public function showProfile()
    {
        // 任意の処理
    }
}

// resource型(RESTの形式に沿う)
class UserController extends Controller
{
    public function index()
    {
        // 一覧取得
    }

    public function store(Request $request)
    {
        // 新規登録
    }
}

resource型は、ルーティングとの連携も効率的で、Route::resource()一行で登録が済みます。設計がRESTに近い場合はこちらを選ぶと自然です。

__invokeを使うシングルアクションの特徴

Laravelでは1アクションだけを担う専用コントローラーとして、__invokeを使う形式も選べます。

これはメソッドを1つだけに限定するため、非常に明快な構造になります。

class UpdateUserProfileController extends Controller
{
    public function __invoke(Request $request)
    {
        // 単一処理のみ実装
    }
}

この形式はフォーム更新や通知送信など、「名前の通りの処理しかしない」場面で活躍します。

テストもしやすく、責務がはっきりするので、小さな処理ほど効果を感じやすいです。

artisan make:controllerのオプション整理

Laravelではartisanコマンドで各種コントローラーを自動生成できます。オプションを理解しておくと、意図した形式の雛形を素早く用意できます。

php artisan make:controller UserController         # 通常
php artisan make:controller UserController --resource  # RESTful構成
php artisan make:controller UpdateProfileController --invokable  # シングルアクション

--resource--invokableは地味に便利なので、個人的には常用しています。


それぞれのコントローラーはどんな場面で使うべきか

コントローラーの種類を知っても、実際に「どれをいつ使うべきか」は常に迷うポイントだったりします。特にWeb画面を扱うのか、APIとして設計するのかで選び方も変わってきます。

以下、実務で判断に悩んだときの観点をいくつか紹介します。

画面系 vs API系での判断ポイント

「画面用だから通常型」「APIだからresource型」と単純に分けられないのが実際のところ。実務では、レスポンス形式や責務の大きさ、エンドポイントの数など、いくつかの観点を掛け合わせて選ぶのが自然です。

以下の表に、それぞれの特性と相性の良いコントローラー形式を整理しました。

判断軸画面系(Web)API系
レスポンス形式HTMLビュー(view()redirect()JSONレスポンス(response()->json()
ルーティング設計画面単位で自由に定義されがちRESTfulなエンドポイント構成が前提
状態の扱いセッションやCSRFなど状態を保持ステートレス通信が基本
アクションの粒度複数の処理をまとめる傾向(通常型向き)単機能ならinvokableが有効な選択肢
推奨される形式通常型、resource型(柔軟に使い分け)resource型、invokable(明快な責務)

たとえば、Web画面で使うフォームの表示+登録処理を1つのクラスで扱いたいなら「通常型」が合いますし、APIとしてユーザー一覧や詳細などREST構造がそのまま当てはまる場面では「resource型」がベストです。

ただし、「APIだからresource型一択」と思い込むのは危険です。たった1つの処理しかない場合(例:通知送信、ログ出力、状態切替など)は、むしろinvokableで切り出した方が読みやすく、テストしやすくなります。

最終的には「どの形式なら処理の責務がはっきりするか」「読み手が迷わず追えるか」を基準に選ぶのが、保守性の面でも長く効いてきます。

RESTful設計との関係を意識する

resource型は「RESTfulに設計する」という前提の上で効力を発揮します。

たとえば「一覧取得はGET /users」「新規登録はPOST /users」といったように、HTTPメソッドとURLの組み合わせが明快であることが重要です。

実際にはRESTと完全一致しないケースも多いため、「REST的な構造を守るべきか、例外として自由にするか」の判断が求められます。そこはチーム内の設計方針にも関わる部分なので、統一感が大切になります。


実装の工夫と保守性を高めるポイント

コントローラーはあくまで「ルーティングとビジネスロジックの橋渡し役」です。

その責務を意識すると、共通処理の分離やサービスクラスの導入が自然な流れとして見えてきます。

共通処理の整理とサービスクラスの使いどころ

バリデーションやDB操作などを直接コントローラーに書くと、再利用性が低くなりがちです。そこで役立つのが、専用のサービスクラスを設けて処理を切り出す方法です。

namespace App\Services;
use App\Models\User;

class UserService
{
    /**
     * ユーザーを新規作成する
     */
    public function createUser(array $data)
    {
        // DBに新しいユーザーを登録
        return User::create($data);
    }
}
use Illuminate\Http\Request;
use App\Services\UserService;

class UserController extends Controller
{
    public function store(Request $request, UserService $service)
    {
        // バリデーション済みデータでユーザー作成処理を実行
        $service->createUser($request->all());

        // 登録完了後、ユーザー一覧画面にリダイレクト
        return redirect()->route('users.index');
    }
}

このように役割を分けることで、テストしやすく、責任範囲も明確になります。コードが追いやすくなるので、他の開発者との連携も取りやすくなります。

ルーティングで混乱しないためのコツ

resource型を使う場合、とくに気をつけたいのがルーティングの命名やパスの構造です。

自動生成されるルート名を把握せずに使っていると、ビューやリダイレクト先で迷子になりがちです。

Route::resource('users', UserController::class);

この場合、users.indexusers.create などのルート名が自動で設定されます。Bladeテンプレートでリンクを作る際にも、これを把握していないとミスが起きやすいです。

意識的に route:list コマンドを活用し、ルートの全体像を確認しながら開発を進めるのがコツです。


まとめ

コントローラー設計を工夫することで、処理の責任範囲が明確になり、可読性や保守性が大きく向上します。

Laravelが提供する3つの形式を「どれが正解か」ではなく、「どの場面に合うか」で選ぶ視点が大切です。私自身もinvokable型の導入でテストしやすさや見通しの良さを実感しました。

すべてを完璧に整理する必要はありませんが、「読み手のことを考える」姿勢を持って設計することが、より良い開発体験につながるはずだと思います。

この記事を書いた人

業務システムとWebアプリの開発に20年以上携わるフリーランスエンジニア。
製造業や物流業界のシステム保守・改修を中心に、要件定義から運用改善まで幅広く対応してきました。Laravelや業務改善、AI活用など、現場で実際に試し・使い続けている技術や設計の工夫を、トラブル対応の視点も交えてブログに記録しています。

日々の業務で直面した「困ったこと」をベースに、再現性のあるノウハウをシンプルな言葉で伝えることを意識しています。

目次