Laravelに触れたとき、「こんなに簡単なのか…!」と驚く瞬間ってありますよね。
たとえば、以下のようなControllerの一例を見てみましょう。
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required',
'body' => 'required',
]);
Post::create($validated);
return response()->json(['message' => '投稿完了']);
}
入力のバリデーション、DBへの保存、レスポンス返却――ほんの数行で一連の処理が完結してしまいます。初めて触れる人なら「これこそLaravelのすごさだ!」と感動するのも無理はありません。
しかし、この感動を頼りに進めていくと、次第にControllerのコード量はどんどん増えていきます。はじめはシンプルだったはずなのに、気づけば複雑な機能が集中する“重たいController”になってしまうケースも少なくありません。
この記事では、自分自身の「Laravel Controller体験記」をもとに、
- なぜ最初にControllerに感動するのか
- なぜ行き過ぎたControllerが生まれてしまうのか
- どうやってControllerをうまく扱うべきか
について整理していきます。
次章では、改めてLaravelの「MVC」の「C」にあたるControllerがどのような立場・役割を担っているのかを見直してみましょう。
- 1. Controllerの役割ってそもそも何だっけ?
- 1.1. 🚦 ControllerはLaravelの“交通整理役”
- 1.2. 👷 Controllerは「司令塔」、でも現場作業員ではない
- 1.3. 🤝 Controllerに全部書きがちな理由(あるある体験)
- 2. Controllerだけで「入力から出力まで」を書いてみる
- 2.1. Step 1:まずは「Postモデル」を作成する
- 2.2. Step 2:マイグレーションでテーブルの構造を定義する
- 2.3. Step 3:Modelに保存可能な項目を指定する
- 2.4. Step 4:Controllerで一連の処理を行う(storeメソッド)
- 2.5. 完成したController(コード全体)
- 2.6. 動かしたい場合
- 2.6.1. web.phpにルートを追加する
- 2.6.2. curlで投稿してみよう
- 2.6.3. 保存されたデータを確認しよう
- 2.7. 次へ進む前に
- 3. 『何でもController』が抱えるリスクと、脱却のステップ
- 3.1. 🙃 よくある「詰め込みすぎController」の例
- 3.2. 👓 Controllerは「自分でやる」から「任せる」へ
- 3.3. 「全部できる」のがLaravelの強み。だけど、そこから一歩先へ進もう。
- 4. 次に進むときのヒント
Controllerの役割ってそもそも何だっけ?
LaravelはMVC(Model-View-Controller)という設計思想に基づいて構築されたフレームワークです。
MVCの中でもControllerの役割は、「ユーザーから受け取ったリクエストに応じて、アプリケーションが何をすべきかを決めること」にあります。
🚦 ControllerはLaravelの“交通整理役”
ユーザーがフォームを送信したり、ページにアクセスしたりすると、必ず何らかの処理が必要になります。
その処理を「どこで・どうやって実行するか」を判断しているのがControllerです。
たとえば、以下のようなルーティングを定義すると:
Route::post('/posts', [PostController::class, 'store']);
この記述で、特定のURL /posts
に対して、どのControllerのどのメソッドが呼ばれるのかを明示的に指定できます。
つまり、Controllerがアプリケーションの流れを整理し、処理を的確な場所へと導いてくれるわけです。
👷 Controllerは「司令塔」、でも現場作業員ではない
ただし、Controllerが「なんでもできるからといって、すべてを自分でやる存在」ではありません。
LaravelのControllerはあくまで処理の指揮官であって、自らがすべてのロジックを抱える必要はないのです。
Controllerは主に以下のような役割を担います:
- 必要なバリデーションを呼び出す
- データ操作はModelなど他のクラスに任せる
- ユーザーに返すレスポンス(ViewやJSONなど)を決定する
つまり、「何をするか」を決めるのがControllerであり、実際の作業は別のクラスに任せるのが理想的な姿です。
🤝 Controllerに全部書きがちな理由(あるある体験)
ただし、Controllerが「なんでもできるからといって、すべてを自分でやる存在」ではありません。
LaravelのControllerはあくまで処理の指揮官であって、自らがすべてのロジックを抱える必要はないのです。
Controllerは主に以下のような役割を担います:
- 必要なバリデーションを呼び出す
- データ操作はModelなど他のクラスに任せる
- ユーザーに返すレスポンス(ViewやJSONなど)を決定する
つまり、「何をするか」を決めるのがControllerであり、実際の作業は別のクラスに任せるのが理想的な姿です。
Controllerだけで「入力から出力まで」を書いてみる
Laravelに初めて触れたとき、「意外と簡単に動くんだな」と感じる瞬間があります。
フォームの入力を受け取り、データを保存して結果を返す、といった処理も、Controllerだけで完結できるからです。
ここでは、「フォームから送信されたデータを受け取り、データベースに保存して結果を返す」流れを、シンプルに整理していきましょう。
Step 1:まずは「Postモデル」を作成する
Laravelでデータを操作するときは、「Model」というクラスを使います。
今回は例として、「投稿(Post)」というデータを扱ってみます。
次のartisanコマンドでPostモデルを作成します。
php artisan make:model Post -m
このコマンドで、次の2つのファイルが生成されます。
app/Models/Post.php
(投稿データを扱うModel)database/migrations/xxxx_xx_xx_create_posts_table.php
(テーブル作成用のマイグレーション)
Step 2:マイグレーションでテーブルの構造を定義する
マイグレーションファイルに、保存したい情報を以下のように記述します。
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
ファイルを保存したら、以下のコマンドでテーブルを作成します。
php artisan migrate
Step 3:Modelに保存可能な項目を指定する
Laravelでは、安全にデータを保存するために、どのカラムを保存可能にするかをModelで明示的に指定します。
そのために、$fillable
プロパティを設定します。
// app/Models/Post.php
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'body'];
}
💡 補足:HasFactoryについて
use HasFactory;
は、テストやダミーデータ作成時に便利な機能です。
今回の保存処理だけであれば特に意識する必要はありませんが、Laravelが推奨するのでそのまま記述しておきます。
Step 4:Controllerで一連の処理を行う(storeメソッド)
Controllerの役割は、リクエストを受け取り、バリデーションし、データを保存し、レスポンスを返すことです。
この一連の流れを、store()
メソッドにまとめます。
public function store(Request $request)
{
// 入力データのバリデーション
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
]);
// データベースに保存
$post = Post::create($validated);
// 結果をJSON形式で返却
return response()->json(['message' => '投稿が完了しました。']);
}
store()
メソッドでは次の3つを順番に行っています。
- バリデーション
入力内容のチェック($request->validate()
) - 保存処理
バリデーション済みデータの保存(Post::create()
) - レスポンスの返却
JSONで結果を返す(response()->json()
)
完成したController(コード全体)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
class PostController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
]);
$post = Post::create($validated);
return response()->json(['message' => '投稿が完了しました。']);
}
}
このように、LaravelではControllerひとつで「入力→保存→レスポンス」の流れが完結します。
少ないコードで多くの処理が実現できることこそ、Laravelの魅力の一つです。
ただし――
このままControllerに何でも書いていくと、コードは少しずつ重く、複雑になっていきます。
次章では、「Controllerに全部書いてしまうと、どんな問題が起こるのか?」を見ていきましょう。
動かしたい場合
先ほどのコードを動かしたい場合は以下実行お願いします。
web.phpにルートを追加する
routes/web.php
に、さきほど作成した store()
メソッドを呼び出すルートを追加します。
use App\Http\Controllers\PostController;
Route::post('/posts', [PostController::class, 'store']);
curlで投稿してみよう
curl -X POST http://localhost/posts \
-H "Content-Type: application/json" \
-d '{"title": "初めての投稿", "body": "これはテスト投稿です。"}'
無事成功すれば以下のようなレスポンスが返ります。
{
"message": "投稿が完了しました。"
}
Controllerの動作が確認できました。
保存されたデータを確認しよう
投稿が本当に保存されているか、Laravelの対話ツール tinker
を使って確認してみましょう。
php artisan tinker
>>> App\Models\Post::all();
保存された投稿が表示されれば、データベースにもきちんと反映されています。
Laravelは、Controllerひとつでここまで完結できる――それを実感できたら、もう一歩Laravel使いに近づいています。

次へ進む前に
ここまでで、Controllerの中に「バリデーション → 保存 → レスポンス」を書くことで、ひととおりのアプリ的な流れができるという体験ができました。
でも、全部Controllerに書いてもいいんだっけ?
このまま増えていく処理は、将来的にどうなるんだろう?
次章では、その疑問に向き合いながら、「Fat Controller」問題へと話を進めていきます。
『何でもController』が抱えるリスクと、脱却のステップ
LaravelのControllerは非常に優秀で、リクエストの受付、入力のバリデーション、データ保存、レスポンスの返却までをシンプルに行えます。
そのため、開発初期はControllerに処理を集中させることも珍しくありません。
しかし、開発を続けていくうちに、Controllerにどんどん処理が追加され、コードが徐々に膨らんでいきます。その結果、「一目で何をしているか把握しづらい」Controllerが出来上がってしまうのです。
🙃 よくある「詰め込みすぎController」の例
実際のプロジェクトでもよく見かける、典型的な例を見てみましょう。
public function store(Request $request)
{
if (! $request->user()->can('create', Post::class)) {
abort(403, 'Unauthorized');
}
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
'send_email' => 'boolean',
]);
try {
$summary = Http::post('https://api.*****.com/*******', [
'text' => $validated['body']
])->json()['summary'] ?? '';
$post = Post::create([
'title' => $validated['title'],
'body' => $validated['body'],
'summary' => $summary,
]);
if (!empty($validated['send_email'])) {
Mail::to($request->user())
->send(new \App\Mail\PostCreated($post));
}
dispatch(new \App\Jobs\LogPostCreationJob($post->id));
if ($request->routeIs('posts.store.web')) {
return redirect()->route('posts.index')->with('status', '投稿完了');
}
return response()->json(['message' => '投稿が完了しました。', 'id' => $post->id]);
} catch (\Throwable $e) {
Log::error('Post保存エラー', ['error' => $e->getMessage()]);
return response()->json(['message' => 'エラーが発生しました'], 500);
}
}
一見すると問題ないようにも見えるかも知れません。しかし、これは問題大有りです。
- 処理が多すぎて一目で何をやっているのかわからない
- 小さな仕様変更でも影響範囲が広くなりやすい
- テストが書きにくく、再利用性も低い
- 保守すること自体がストレスになる
👓 Controllerは「自分でやる」から「任せる」へ
このような問題は、Laravelが便利で柔軟だからこそ起こり得ます。
つまり、Laravelの利便性が裏目に出てしまった状態です。
もちろん、これはLaravel自体の問題ではありません。むしろ、「最初にこれだけの処理をControllerで書ける」こと自体がLaravelの強みです。ただし、そこから一歩進んで、「Controllerをもっと軽く、整理された存在にする」という考え方を導入すると、コードは自然と整理されていきます。
「全部できる」のがLaravelの強み。だけど、そこから一歩先へ進もう。
LaravelのControllerが最初から万能なのは、このフレームワークの魅力です。 初めて動くものを作れたときの感動や手軽さは、そのまま開発を加速してくれます。
しかし、プロジェクトが進み、処理が増えていくにつれて、「コードが複雑になりすぎて管理しきれない」という問題に直面するでしょう。
そんな時、以下のように責任を分割することも一つの選択肢かも知れません。
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Http\Resources\PostResource;
use App\UseCases\CreatePostUseCase;
use Illuminate\Http\JsonResponse;
class PostController extends Controller
{
public function store(StorePostRequest $request, CreatePostUseCase $useCase): JsonResponse
{
$post = $useCase->execute($request->validated(), $request->user());
return new PostResource($post);
}
}
- validationはRequestクラスへ
- メインロジックはUseCaseへ
- Responseの生成はResoucrceへ
こうして役割を分けることで、Controllerは「何をするかを判断し、それぞれの処理を適切な場所へ任せる」存在になります。
LaravelのControllerは最初に頼れる存在でありながら、プロジェクトの成熟とともに自然に後ろへ下がり、コード全体の流れを整理する立場へと変化していきます。 その変化を受け入れることで、プロジェクト全体のコードが読みやすく、保守しやすくなっていくのです。
次に進むときのヒント
最後に紹介した責務の分割は、決して「正しい形」を押し付けるものではありません。
「Controllerに処理を詰め込みすぎて困ってきたな」 「これ、もうちょっと分けられないかな」
そう思ったタイミングで、少しずつ取り入れていけば十分です。
Laravelは自由度が高いぶん、設計も段階的に育てていけるフレームワークです。便利さと整理のバランスを取りながら、自分たちに合ったスタイルを探してみてください。
そして次の章では、その最初のステップとして「Requestクラスを使ってバリデーションをControllerの外に出す」方法を紹介していきます。
Controllerを少し軽くしてみる。その小さな一歩から、コードの見通しが変わっていくかもしれません。
コメントを残す