【Laravel】 Requestを継承してバリデーションをする

リクエストで入ってきた値は、その値が正常な値かどうかをチェックするのが普通です。Laravelでは、楽にバリデーションを行える機構が存在しています。

バリデーションする

Controllerでバリデーションする場合、$request->validate([/* ... */])を用います。

バリデーションルールは連想配列で指定し、キーに入力欄名を、値にルールを配列か|区切りの文字列で並べます。

public function postData(Request $request)
{
    $request->validate([
        'title' => ['required', 'between:4,64'],
        'content' => ['required'],
    ]);

    // ...
}
$request->validate([
    'title' => 'required|between:4,64',
    'content' => 'required',
]);

ルール一覧はLaravelのドキュメントを参照してください。https://readouble.com/laravel/9.x/ja/validation.html#available-validation-rules

バリデーションルールはかなり数が多いため、よく使用するものだけ載せておきます。

名前役割
required必須項目
min max between:min,max入力の長さや数値の範囲等。betweenbetween:4,64のように書く
email文字列がemailの形式かどうか
date日付かどうか+日付として正しいかどうか

だいたいこれで事足りると思います。

条件付きでバリデーション

その項目がフォームに存在する場合のみチェックする、特定の項目が存在すればチェックしないなど、条件付きでバリデーションを行うことができます。

$request->validate([
    // bailは、失敗したもの以降をチェックしないもの。下の例では、alpha_dashに失敗したらbetweenはチェックしない
    'title' => 'bail|required|alpha_dash|between:4,64',
    'content' => 'required'
    
    // sometimesは、フィールド自体がなければチェックしない
    'num' => 'sometimes|integer|between:1,100',

    'is_share' => 'required|boolean',
    // is_shareがfalseの場合は飛ばす (excludeする)
    'share_range' => 'exclude_if:is_share,false|required',
]);

バリデーションをカスタマイズ

デフォルトで用意されているバリデーションでは対応できないような場合、ルールを配列で並べ、その中に関数を含めます。

関数内では第1引数に入力欄のnameが、第2引数のその入力欄の値、第3引数には失敗時に呼び出すための関数が入ります。

この関数内で他の値を参照したい場合、普段どおり$request->get('name');のようにします。(関数でuseするのを忘れないようにする)

$request->validate([
    'content' => ['required', function ($name, $item, $fail) use(&$request) {
        Log::debug($name); // 'content'
        Log::debug($item); // 入力した値

        // useすると$request->input等で他の入力値も参照できる
        Log::debug($request->input($name));

        if (strpos($item, '<script>') !== false) {
            // バリデーションに引っ掛ける場合は $fail('メッセージ') を呼び出す
            $fail('本文にscriptタグを含めることはできません');
        }
    }],
]);

バリデーションのエラー表示

バリデーションに引っかかると、Laraveは自動的に文章を生成します。生成した文章は、blade側で$errorsに格納されます。

@if ($errors->any()) <!-- 何らかのエラーがあるかどうか -->
    <div class="alert alert-danger">
        <ul>
            <!-- 全てのエラーをforeachで出力 -->
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

これらのバリデーションエラーで表示されるメッセージは、app/lang/en/validation.phpに入っています。

FormRequestを拡張する

Controllerにバリデーションを書いても良いですが、複雑になると収集がつかなくなります。また、バリデーションを使い回せません。

そこで、FormRequestクラスを継承したクラスを作りましょう。

php artisan make:request SamplePostRequest

これを実行すると、app/Http/Requestsディレクトリ内にファイルが作成されます。

バリデーションのルール

rulesの戻り値の連想配列は、Controllerでvalidateを使用した時と同様にキーに入力欄名、値にバリデーションルールを記載します。複数のルールがある場合、それらを|で区切るか、配列で書きます。

public function rules()
{
    return [
        'title' => 'required|between:4,64',
        'num' => 'required|integer|between:1,100',
        'content' => 'required'
    ];
}
public function rules()
{
    return [
        'title' => ['required', 'between:4,64'],
        'num' => ['required', 'integer', 'between:1,100'],
        'content' => ['required'],
    ];
}

バリデーションをカスタマイズ

基本的にはコントローラでした場合と同じですが、他の値を参照したい場合、$this->request->get('name');のようにします。

public function rules()
{
    return [
        'content' => ['required', function ($name, $item, $fail) {
            Log::debug($name); // 'content'
            Log::debug($item); // 入力した値

            // $this->request->get で他の入力値も参照できる
            Log::debug($this->request->get($name));

            if (strpos($item, '<script>') !== false) {
                // バリデーションに引っ掛ける場合は $fail('メッセージ') を呼び出す
                $fail('本文にscriptタグを含めることはできません');
            }
        }],
    ];
}

バリデーションのメッセージ

バリデーションで引っかかった場合の表示をカスタマイズできます。

新たにmessagesメソッドを作成し、その戻り値に連想配列を用意します。

連想配列には、'入力名.バリデーションルール' => '表示メッセージ'のように書きます。ここにはないバリデーションエラーの場合、デフォルトのメッセージが使用されます。

public function rules()
{
    return [
        'title' => 'required|between:4,64',
        'num' => 'required|integer|between:1,100',
        'content' => 'required'
    ];
}

public function messages()
{
    return [
        'title.required' => 'タイトルは必須です',
        'title.between' => '文字数は4~64文字に設定してください',
        'num.between' => '数値は1~100に設定してください',
    ];
}

バリデーション失敗時のリダイレクト先

デフォルトでは元いたページに戻されます。もし別のページに移動させたい場合は、$redirectに代入します。

// バリデーション失敗時の移動先
protected $redirect = '/home';
// ルート名を利用した移動
protected $redirectRoute = 'form.error';

// Route::get('/err', [/*...*/])->name('form.error');
// コントローラを利用した移動
protected $redirectAction = 'App\Http\Controllers\HelloController@err';

リクエストの承認機能

リクエストを投げたユーザが、そのリクエストの使用を承認されているかをauthorizeメソッドで判定できます。何かややこしいことをするわけでもなく、trueを返せば認可、falseを返せば不認可ということになります。

public function authorize()
{
    // 常に認可する
    return true;
}
public function authorize()
{
    $isLoggedIn = false;
    try {
        // ...
    } catch ( Exception $ex ) {
        // ...
        return false;
    }

    return $isLoggedIn;
}

入力された値を復元する

バリデーションに失敗して元のページに戻ると、入力が消えていることがあります。ブラウザが復元してくれればよいですが、復元できるとは限りません。

そこで、old('name')を用いて復元できます。

<label>
    title:
    <input type="text" name="title" value="{{ old('title') }}">
</label>
<label>
    content:
    <textarea name="content" cols="10" rows="5">{{ old('content') }}</textarea>
</label>

バリデーションでは自動で保存されますが、それ以外でセッションに入力を一時保持したい場合、内部で$request->flash()すると可能です。$request->flashOnly(['title', 'content'])flashExcept('password')のように、flash対象を選ぶこともできます。

まとめ

フォームリクエストを処理する際にバリデーションは必要不可欠です。バリデーションの共通化をするには、できるだけFormRequestを継承したクラスに書くと、Controllerの肥大化を防ぐことができます。

関数をバリデーションルールに入れることで複雑なバリデーションもコントローラに入れずに済みます。

laravel validation thumb

役に立ったらシェアしよう!