5.5より前のバージョンのLaravelでプログラムをしていたひとたちに取っては、5.5で登場したルールオブジェクトは神の恵みと言ってもいいくらい。カスタムバリデーションを作成するために、Validator::extendはどこに宣言するの?とか、グローバルでどう作成したバリデーションを共有するの?とか、取り掛かる前に悩んでいたのがウソのよう。新登場のルールオブジェクトのおかげで、カスタムルールの作成が楽しくなりました。

カスタムルールが必要となるときは、たくさんあります。なぜなら既存のバリデーションでは簡単にいかないルールが世の中にたくさんあるからです。

例えば、登録したユーザーのリストが欲しいとして、開始日を指定して今日までに登録したユーザーのみとします。しかし、開始日は今日から2か月までしか遡れない、とします。以下のようなカレンダーで開始日をしてもらいますが、開始日が2か月以上前ならバリデーションでチェックしてエラーを表示としたいです。


このバリデーションには既存のバリデーションのafter_or_equalが使えます、しかし渡すパラメータには、「今日マイナス2か月」が必要です。もちろん前もって計算して渡すこともできます。

Psy Shell v0.9.9 (PHP 7.2.16 — cli) by Justin Hileman
>>> $from = \Carbon\Carbon::today()->subMonth(2)->toDateString();
=> "2019-01-29"
>>> $input = ['start_date' => '2019-01-01'];
=> [
     "start_date" => "2019-01-01",
   ]
>>> validator($input, ['start_date' => 'required|date|after_or_equal:$from'])->errors()->all();
=> []
>>> validator($input, ['start_date' => "required|date|after_or_equal:$from"])->errors()->all();
=> [
     "start dateは2019-01-29以後の日付が必要です",
   ]
>>> 

しかし、もしかしたら2か月でなく3ヶ月となるかもしれないし、一般的なバリデーションがあってもいいですね。こんな感じで呼び出したいです。


['start_date' => ['required', 'date', new RestrictPeriodRule(2)]];

そこで、ルールオブジェクトの出番です。

まず、コマンドでルールのファイルを作成します。


$ php artisan make:rule RestrictPeriodRule

中身を以下のように編集します。

<?php

namespace App\Rules;

use Carbon\Carbon;
use Illuminate\Contracts\Validation\Rule;

class RestrictPeriodRule implements Rule
{
    protected $months;
    protected $start;

    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct($months)
    {
        $this->months = $months;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $this->start = Carbon::today()
                  ->subMonth($this->months)
                  ->toDateString();

        return ($value >= $this->start);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return $this->start.'以降を指定してください';
    }
}

出来たところでテストしてみましょう。

Psy Shell v0.9.9 (PHP 7.2.16 — cli) by Justin Hileman
>>> $input = ['start_date' => '2019-01-01'];
=> [
     "start_date" => "2019-01-01",
   ]
>>> validator($input, ['start_date' => ['required', 'date', new App\Rules\RestrictPeriodRule(2)]])->errors()->all();
=> [
     "2019-01-29以降を指定してください",
   ]
>>> 

By khino