laravel5.8¦独自のバリデーションを作成!

4 min read読了の目安(約3600字

概要

laravelのバリデーションでカバーが難しい特殊なバリデーションをかけたいときに、独自のバリデーションを作成してカスタマイズしていきます!

今回は2パターンのバリデーションについて…
・クロージャの使用
・Ruleオブジェクトの作成

環境

・laravel 5.8
・php 7.2

パターン1:クロージャのバリデーション

例えば合計金額amount、事前払いprepayment、利用ポイントpoint がある場合に、合計金額がマイナスにならないようにしたいとします。

public function rules()
{
  return [
    'amount' => 'required|integer',
    'prepayment' => 'required|integer',
    'point' => ['required','integer',
                  function($attribute, $value, $fail) {
                    // 入力の取得
                    $inputData = $this->all();
                    if (($inputData['amount'] - $inputData['prepayment'] - $inputData['point']) < 0)
                    {
                        $fail('合計金額が、事前払い、または利用ポイントより多くなるようにしてください。');
                    }
                  }],
  ];
}
💡withValidatorのafterを使用することで、最後に行いたいバリデーションを書くことができます

最後にバリデーションしたい条件がある場合や、上の書き方だとごちゃごちゃして嫌だなという時は便利かもしれません。

public function rules()
{
    return [
        'amount' => 'required|integer',
        'prepayment' => 'required|integer',
        'point' => 'required|integer'
    ];
}

public function withValidator(\Validator $validator) : void
{
    if ($validator->fails()) {
        return;
    } // bailと同じ効果

    $validator->after(function ($validator) {
        $inputData = $this->all();
        if (($inputData['amount'] - $inputData['prepayment'] - $inputData['point']) < 0)
        {
            $validator->errors()->add('error', '合計金額が、事前払い、または利用ポイントより多くなるようにしてください。');
        }
    });
}
  

パターン2:Ruleオブジェクトの作成

商品販売サイトで、データベースに店舗と商品のテーブルが1対多関係で存在していたとして、店舗によって販売している商品が異なる場合、リクエストで送られてくる店舗+商品の組み合わせで販売可能か?を例にします。

use App\Facility;
use Illuminate\Contracts\Validation\Rule;

class FacilityAndItemPatternExists implements Rule
{
    protected $itemName;

    public function __construct(int $facilityId)
    {
        $facility = new Facility;
        $this->itemName = $facility->where('facility_id', $facilityId)
                                   ->join('items', 'facilities.item_id', '=', 'items.id')
                                   ->pluck('items.name')
                                   ->toArray();
    }

    public function passes($attribute, $value)
    {
        return in_array($value, $this->itemName, true);
    }

    public function message()
    {
        return ':attributeは選択できません。';
    }
}

今回の例は毎回データベースを見に行ってしまうので別の問題がありますが…( ´ ` )
複雑なパターンを指定できます。

💡呼び出し方
使用頻度が高い場合はサービスプロバイダに登録すると思いますが、サービスプロバイダ登録をしない場合

public function shopValidationRules($arr)
{
  return \Validator::make($arr, [
      'facility_id' => 'required|integer',
      'item_name'   => ['required', 'integer', new FacilityAndItemPatternExists($arr['facilityId'])],
      'detail'      => 'nullable|string'
    ],[
      'required' => ':attributeが入力されていません。',
      'integer'  => ':attributeは数値を入力してください。'
    ],[
      'facility_id' => '施設',
      'item_name'   => '商品',
      'detail'      => '詳細'
    ]);
}


validator::makeの場合、第4引数に今回のバリデーションのattribute名を設定することができます。第4引数がない場合はvalidation.phpなどに指定したattribute名になります。

💡補足
Ruleオブジェクトの場合は複数の条件でチェックをすることもできます🌙

    public function passes($attribute, $value)
    {
        // ユーザー権限によってエラーをスルー
        if (\Gate::allows('system-manager')) {
            return true;
        }

        // 店舗名が存在しているか
        if (in_array($value, $facilityNames, true)) {
            return true;
        }

        // 全てに該当しない
        return false;
    }

Ruleオブジェクトの例の内容はあまりないかもしれませんが…外部データ取り込んでDBに保存するときに複雑なバリデーションを設定でき便利でした◎

バリデーションでユーザーによってバリデーションの内容を変えたいとき、gateを設定していると簡単に実装できておもしろいなと思います◡̈