1. Home
  2. PHP
  3. Laravel
  4. Laravel のフォームリクエストクラスでバリデーションロジックをコントローラから分離する

Laravel のフォームリクエストクラスでバリデーションロジックをコントローラから分離する

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:PHP,Laravel,artisan,Beginner,validation,FormRequest
Laravel のフォームリクエストクラスでバリデーションロジックをコントローラから分離する

Laravel ではフォームを送信した際に、バリデーションメソッドを使い入力値が適正であるかどうかをチェックできますが、実際の開発現場ではフォームの項目が多かったり、複雑なバリデーションが必要だったりなど、コントローラにそのままバリデーションを書くと無駄に肥大化してしまい、数か月後に見て萎えるなんて状況も少なくはありません。

そこで今回は、フォームリクエストクラスを用いてバリデーションロジックをコントローラから分離させます。ソースコードの見通しが各段に良くなるほか、バリデーションの定義のみに集中できます。

Contents

  1. 開発環境
  2. フォームリクエストクラスの生成
  3. フォームリクエストクラスの実装
  4. コントローラの変更
  5. 動作確認
  6. エラーメッセージのカスタマイズ
  7. 独自バリデーションの追加
  8. 権限バリデーション

開発環境

開発環境に関しては以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.2
  • Laravel 5.6

Laravel のルートディレクトリを「laravel/」とします。

尚、通常のバリデーションメソッドを用いたバリデーションの基本形は以下を参考にしてください。
Laravel の validation メソッドでバリデーションを実装する入門編(日本語エラーメッセージ付き)

フォームリクエストクラスの生成

まずは、フォームリクエストクラスを作成します。 laravel ルートディレクトリに移動し、以下の artisan コマンドを叩きます。

# laravel のルートディレクトリへ移動
cd /path/to/laravel

# リクエストクラス生成
php artisan make:request SampleValiRequest

# 実行結果
[demo@localhost laravel]$ php artisan make:request SampleValiRequest
Request created successfully.

app/Http/Requests 配下へ SampleValiRequest.php が生成されます。

laravel
 ├─ app
 │   ├─ Http
 │   │   └─ Requests
 │   │        └─ SampleValiRequest.php
laravel/app/Http/Requests/SampleValiRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class SampleValiRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

フォームリクエストクラスの実装

それでは、生成したフォームリクエストクラスにバリデーションを実装していきます。

laravel/app/Http/Requests/SampleValiRequest.php
?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class SampleValiRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
          'name' => 'required|string|max:255',
          'gender' => 'required',
          'age' => 'required|integer',
          'file' => 'required|file|image|max:10000',
          'comment' => 'required',
        ];
    }
}

rules() メソッドにバリデーションルールを記述しています。そして、authorize() メソッドでは、ひとまず true を返すようにしておいてください。

コントローラの変更

バリデーションロジックをフォームリクエストクラスへ移したので、コントローラの内容も以下に変更します。

laravel/app/Http/Controllers/ValiController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests\SampleValiRequest;

class ValiController extends Controller
{
  public function index()
  {
    return view('sample_vali');
  }

  public function receiveData(SampleValiRequest $request)
  {
    return view('sample_vali', ['status' => true]);
  }
}

バリデーションロジックが不要なのでとてもすっきりしています。基本形である、コントローラ内でバリデーションメソッドを用いバリデーションを行う 場合と見比べてみてください。

コントローラに追加したのは以下2点です。

use App\Http\Requests\SampleValiRequest;

先ほど作成したフォームリクエストクラスを use し

public function receiveData(SampleValiRequest $request)

今回 POST送信を受ける為に作成した receiveData() メソッドの引数に SampleValiRequest クラスを渡しています。こうする事で、フォームを receiveData へ送信した際に、自動でバリデーションが行われます。つまり、receiveData() メソッドに処理が渡ってくる=フォームリクエストクラスで定義したバリデーションは無事に通過した。という事になります。

ちなみにバリデーションが通らなかった場合は、エラーメッセージを持って送信元のフォームのあるページへリダイレクトされます。つまり自動で戻ってくるという事になります。(HTTP ステータスコードは 422 です)

動作確認

これで作業は完了しました。 http://YOURDOMAIN/sample/vali へアクセスし、実際にフォームを送信してエラーを出してみます。

これでバリデーションロジックの分離は完了です。

エラーメッセージのカスタマイズ

上記キャプチャのエラーメッセージは日本語言語ファイル から出力していますが、場合によってはフォームごとに独自のエラーメッセージを表示したいという状況もわりとあったりします。

そういう場合も、フォームリクエストクラスに追加実装するだけで実現できます。

SampleValiRequest.php に messages() メソッドを追加します。

laravel/app/Http/Requests/SampleValiRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class SampleValiRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
          'name' => 'required|string|max:255',
          'gender' => 'required',
          'age' => 'required|integer',
          'file' => 'required|file|image|max:10000',
          'comment' => 'required',
        ];
    }

    /**
     * エラーメッセージのカスタマイズ
     * @return array
     */
    public function messages()
    {
      return [
        'name.required' => '名前を入力してください',
        'name.string' => '名前は文字列で入力してください',
        'name.max' => '名前は255文字以内で入力してください',
        'gender.required'  => '性別を選択してください',
        'age.required'  => '年齢を選択してください',
        'age.integer'  => '年齢は数字で入力してください',
        'file.required'  => 'ファイルを選択してください',
        'file.file'  => 'ファイルのアップロードに失敗しました',
        'file.image'  => 'アップロード可能な画像は「jpg」「png」「bmp」「gif」「svg」です',
        'file.max'  => 'アップロードするファイルは10MBまでです',
        'comment.required' => 'コメントを入力してください',
      ];
    }
}

見た感じでなんとなくわかったと思いますが、KEY部分は「name」+「ルール」になっています。

'name' => 'required|string|max:255'  'name.required' => '名前を入力してください'

改めて動作確認を行ってみます。

エラーメッセージが変更されました。

独自バリデーションの追加

Laravel に限りませんが、PHP フレームワークで Web アプリケーション開発を行っていると、どうしても機能の限界とやらにぶつかる場合も多々あります。

バリデーションとは「妥当性確認」であり、開発案件によって様々な方向でのバリデーションが必要になる場合もあり、フレームワークの機能の限界というよりは、独自のバリデーション実装が必要になるという方が正しいですが、Laravel の場合、そういった状況でも柔軟に対応できます。

以下の記述のように withValidator() メソッドを実装する事で、独自の処理を追加する事が出来ます。

laravel/app/Http/Requests/SampleValiRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class SampleValiRequest extends FormRequest
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
      return true;
  }

  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
      return [
        'name' => 'required|string|max:255',
        'gender' => 'required',
        'age' => 'required|integer',
        'file' => 'required|file|image|max:10000',
        'comment' => 'required',
      ];
  }

  /**
   * エラーメッセージのカスタマイズ
   * @return array
   */
  public function messages()
  {
    return [
      'name.required' => '名前を入力してください',
      'name.string' => '名前は文字列で入力してください',
      'name.max' => '名前は255文字以内で入力してください',
      'gender.required'  => '性別を選択してください',
      'age.required'  => '年齢を選択してください',
      'age.integer'  => '年齢は数字で入力してください',
      'file.required'  => 'ファイルを選択してください',
      'file.file'  => 'ファイルのアップロードに失敗しました',
      'file.image'  => 'アップロード可能な画像は「jpg」「png」「bmp」「gif」「svg」です',
      'file.max'  => 'アップロードするファイルは10MBまでです',
      'comment.required' => 'コメントを入力してください',
    ];
  }

  /**
   * 独自処理を追加する
   * @param $validator
   */
  public function withValidator($validator)
  {
    $validator->after(function ($validator) {
      if (
        strtotime(date('H:i:s')) >= strtotime('21:00:00')
        ||
        strtotime(date('H:i:s')) <= strtotime('09:00:00')
      ) {
        $validator->errors()->add('field', '投稿できるのは9時から21時までです。');
      }
    });
  }
}

今回は一歩踏み込んだ例として、フォームバリデーションではなく、単純に時刻が 9~ 21 以外であればエラーにする(小学生のテレビゲーム制限)処理を実装しましたが、フォームをさらに検査してもいいし、とにかくここは独自処理を追加できます。

改めて動作確認を行ってみます。

フォームのバリデーションには通過していますが、時間に引っかかったのでエラーになりました。

と、こんな実装も行う事が出来ます。

権限バリデーション

フォームリクエストクラスに authorize() メソッドがありますが、そこでは、ユーザの権限など、認証系のバリデーションを定義する事が出来ます。

例えばリファレンスで紹介されている以下のコードですが

public function authorize()
{
    $comment = Comment::find($this->route('comment'));

    return $comment && $this->user()->can('update', $comment);
}

「あるコメントが表示される、もしくは編集できるページへアクセスしたユーザが、そのコメントの更新権限を持っているか」というバリデーションを行っています。

もちろん、その権限を持っていればバリデーションに通り、持っていなければバリデーションエラーとなります。

他にも、以下のように定義する事も出来ます。

public function authorize()
{
    return Gate::allows('admin-higher');
}

ちなみに authorize() メソッドを使わない場合は、最初にしたように true を返すようにしておきます。このメソッドで true が返らない場合、403 エラーが返ります。

まとめ

作業は以上になります。バリデーションのあるところフォームあり送信データの格納ありと、なかなか数が多くなると気分が重たくなりがちですが、こうしてロジックを分離できるとメンテナンスも楽になるので良いですよね。簡単に実装できるので、是非試してみてください。

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級