【Laravel】Laravelのバリデーション(Validation)の書き方

laravelアイキャッチ Laravel

こんにちは、かつコーチです。

前回は、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,emailusersテーブルの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に切り出すとコントローラが読みやすくなる

次に読むべき記事

バリデーションでエラーを検知できるようになったら、次はエラーメッセージや処理結果をユーザーに伝えるためのセッション・フラッシュメッセージについて学んでいきましょう。

→ 次の記事:セッション・フラッシュメッセージの使い方

タイトルとURLをコピーしました