こんにちは、かつコーチです。
前回は、Requestオブジェクトでフォームの値を受け取る方法を解説しました。
ただ、値を受け取れるようになっても、それがそのまま「正しい値」とは限りません。
メールアドレスの形式が違っていたり、必須項目が空だったり、ユーザーの入力にはさまざまな抜け漏れが起こり得ます。
こうした入力チェックをLaravelで行う仕組みがバリデーション(Validation)です。
今回は、コントローラで手軽に書く方法から、規模が大きくなったときの整理方法まで解説します。
バリデーションとは?
入力値が条件を満たしているかチェックする仕組み
バリデーションとは、送られてきた値が「必須項目が埋まっているか」「文字数は適切か」「メール形式になっているか」といった条件を満たしているかをチェックする処理のことです。
チェックに引っかかった場合は、エラーメッセージと一緒に、直前の画面に自動的に戻す動きをLaravelが用意してくれています。
なぜチェックが必要なのか
「フロントエンド側のJavaScriptでもチェックしているから大丈夫では?」と思う人もいるかもしれません。
しかし、JavaScriptによるチェックはブラウザの開発者ツールで簡単に無効化できてしまいます。
悪意のあるユーザーがフォームを介さず直接リクエストを送ってくることも考えられるため、サーバー側でも必ずチェックする必要があります。
私が新人だった頃、フロント側のバリデーションだけで満足してリリースしてしまい、空の名前で大量の問い合わせデータが登録される、という事故を経験したことがあります。
それ以来、「フロント側のチェックは親切設計、サーバー側のチェックは必須」と考えるようにしています。
コントローラで直接バリデーションする
validate()メソッドの基本
もっとも手軽な方法が、Requestオブジェクトの validate() メソッドです。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|max:50',
'email' => 'required|email',
'message' => 'required|max:1000',
]);
// ここに到達した時点で、$validated には検証済みの値が入っている
return redirect('/contact/complete');
}
}
配列のキーが項目名、値が「ルール」を |(パイプ)区切りで並べた文字列です。
required は必須、max:50 は50文字以内、email はメール形式、という意味になります。
ルールに違反した項目があると、Laravelは自動的に前のページへリダイレクトし、エラーメッセージとフォームの入力値をセッションに保存してくれます。
コントローラ側で「エラーだったら◯◯する」というif文を自分で書く必要がないのが、validate() の便利なところです。
よく使うバリデーションルール一覧
代表的なルールをまとめておきます。
| ルール | 意味 |
|---|---|
required | 必須項目 |
max:50 | 最大文字数(数値の場合は最大値) |
min:3 | 最小文字数(数値の場合は最小値) |
email | メールアドレス形式 |
numeric | 数値のみ |
unique:users,email | usersテーブルのemailカラムで重複していないか |
confirmed | ◯◯_confirmation という項目と値が一致するか |
unique はデータベースへの問い合わせが発生する重めのルールなので、多用しすぎるとフォーム送信のたびにクエリが増える点は覚えておくとよいです。
エラーメッセージをBladeで表示する
バリデーションに引っかかると、エラーメッセージは自動的にセッションの $errors という変数に入り、Bladeテンプレート側で使えるようになります。
<form method="POST" action="/contact">
@csrf
<label>お名前</label>
<input type="text" name="name" value="{{ old('name') }}">
@error('name')
<p class="text-danger">{{ $message }}</p>
@enderror
<label>メールアドレス</label>
<input type="email" name="email" value="{{ old('email') }}">
@error('email')
<p class="text-danger">{{ $message }}</p>
@enderror
<button type="submit">送信</button>
</form>
@error('name') 〜 @enderror で囲むと、name 項目にエラーがあるときだけメッセージが表示されます。
old('name') は、バリデーションエラーで戻ってきたときに、直前に入力した値を再表示するためのヘルパー関数です。
これがないと、エラーで戻ってくるたびに全項目が空欄に戻ってしまい、ユーザーは入力し直しになってしまいます。
つまずきやすいポイント:old()を書き忘れて全消去事故を起こした話
長いフォームで全項目が空になった
私が実務で最初に痛感したのが、この old() の書き忘れです。
10項目以上ある長めの申し込みフォームを作っていたとき、メールアドレスの形式ミスだけでバリデーションエラーになったユーザーが、「名前も住所も電話番号も、全部入力し直しになった」とクレームを送ってきたことがありました。
❌ Before:old()なしでinput要素を書く
<input type="text" name="name" value="">
<input type="text" name="address" value="">
<input type="tel" name="tel" value="">
<input type="email" name="email" value="">
エラーで前の画面に戻ってきても、value="" と固定で書いているため、全項目が空欄に戻ってしまいます。
ユーザー目線では「1箇所ミスしただけなのに、全部消える理不尽な入力フォーム」という体験になってしまいました。
✅ After:全項目にold()を入れて直前の入力を保持する
<input type="text" name="name" value="{{ old('name') }}">
<input type="text" name="address" value="{{ old('address') }}">
<input type="tel" name="tel" value="{{ old('tel') }}">
<input type="email" name="email" value="{{ old('email') }}">
old() を追加したことで、バリデーションエラーで戻っても、入力済みの項目はそのまま残るようになりました。
これ以降、フォームを作るときは「input要素を書いたら、その場で必ず old() もセットする」というのを自分の中のルールにしています。
応用・一歩先の使い方
FormRequestに切り出して整理する
コントローラに書くバリデーションが増えてくると、メソッドが長くなって読みにくくなります。
そんなときは、バリデーションルールだけを専用クラス(FormRequest)に切り出すことができます。
php artisan make:request ContactRequest
<?php
// app/Http/Requests/ContactRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactRequest extends FormRequest
{
public function authorize(): bool
{
return true; // このリクエストの実行を許可するかどうか
}
public function rules(): array
{
return [
'name' => 'required|max:50',
'email' => 'required|email',
'message' => 'required|max:1000',
];
}
public function messages(): array
{
return [
'name.required' => 'お名前を入力してください。',
'email.email' => 'メールアドレスの形式が正しくありません。',
];
}
}
コントローラ側は、Request を作成したFormRequestクラスに差し替えるだけです。
<?php
use App\Http\Requests\ContactRequest;
public function store(ContactRequest $request)
{
$validated = $request->validated();
// バリデーション済みの値を使った処理
return redirect('/contact/complete');
}
コントローラのメソッドに到達した時点で、バリデーションはすでに完了しています。
「コントローラは業務処理に集中し、入力チェックのルールはFormRequestにまとめる」という役割分担ができるため、フォームが複雑な画面ほどこの構成が活きてきます。
まとめ
この記事のポイント
- バリデーションは入力値が条件を満たしているかをチェックする仕組みで、サーバー側での実施が必須
$request->validate([...])を使うと、エラー時のリダイレクトとメッセージ保存を自動でやってくれる- Blade側では
@errorでエラー表示、old()で直前の入力値を再表示する old()を書き忘れると、エラー時に他の入力項目まで消えてしまう- ルールが多い・複雑なフォームは、FormRequestに切り出すとコントローラが読みやすくなる
次に読むべき記事
バリデーションでエラーを検知できるようになったら、次はエラーメッセージや処理結果をユーザーに伝えるためのセッション・フラッシュメッセージについて学んでいきましょう。
→ 次の記事:セッション・フラッシュメッセージの使い方