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.index
や users.create
などのルート名が自動で設定されます。Bladeテンプレートでリンクを作る際にも、これを把握していないとミスが起きやすいです。
意識的に route:list
コマンドを活用し、ルートの全体像を確認しながら開発を進めるのがコツです。
まとめ
コントローラー設計を工夫することで、処理の責任範囲が明確になり、可読性や保守性が大きく向上します。
Laravelが提供する3つの形式を「どれが正解か」ではなく、「どの場面に合うか」で選ぶ視点が大切です。私自身もinvokable型の導入でテストしやすさや見通しの良さを実感しました。
すべてを完璧に整理する必要はありませんが、「読み手のことを考える」姿勢を持って設計することが、より良い開発体験につながるはずだと思います。