フォームリクエストバリデーションで複数のフィールドを1つにまとめてバリデーションする方法


はじめに

2つのフィールドを1つにまとめてバリデーションしたかったけど、
スグにやり方が判らなくてソースを追ってしまったので残しておきます。

前提

  • 年月日と時分秒を別々に指定しないとイケないFormをお持ちの画面。
  • 期間指定したいので、From - Toでフィールド持ってる

こういうヤツ

postされてくるデータは一応以下みたいな感じで説明

入力欄 key
開始日 start_date 年月日
開始時刻 start_time 時分秒
終了日 end_date 年月日
終了時刻 end_time 時分秒

愚直にやるとどうなるか

コード書くの怠いので、文字で流れを書いてしまうと・・・

  • start_dateのフォーマットチェック
  • start_timeのフォーマットチェック
  • end_dateのフォーマットチェック
  • end_timeのフォーマットチェック

かつ、バリエーション終わった後か、withValidator的なヤツで、

  • start_dateとstart_timeをくっつけてCarbonか何かの日付系インスタンス取得
  • end_dateとend_timeをくっつけてCarbonか何かの日付系インスタンス取得
  • 取得した日付系のインスタンスを比較

怠い&冗長。
あと多分_dateと_timeをくっつけた後でフォーマットチェックもしないとイケない気がする。
(withValidatorってvalidationがfailすると呼ばれないっけ?なら不要)

ソリューション

Illuminate\Foundation\Http\FormRequest::validationDataメソッドを上書きする


/**
 * @inheritdoc
 */
protected function validationData()
{
    $this->addStartAt();
    $this->addEndAt();
    return parent::validationData();
}

/**
 * start_dateとstart_timeからstart_atを作ってリクエストパラメータに追加する
 */
private function addStartAt()
{
    $start_date = (string)$this->request->get('start_date');
    if ($start_date === '') {
        return;
    }
    $start_time = (string)$this->request->get('start_time');
    if ($start_time === '') {
        $start_time = '00:00:00';
    }
    $start_at = $start_date . ' ' . $start_time;
    $this->request->add(['start_at' => $start_at]);
}

/**
 * end_dateとend_timeからend_atを作ってリクエストパラメータに追加する
 */
private function addEndAt()
{
    $end_date = (string)$this->request->get('end_date');
    if ($end_date === '') {
        return;
    }
    $end_time = (string)$this->request->get('end_time');
    if ($end_time === '') {
        $end_time = '23:59:59';
    }
    $end_at = $end_date . ' ' . $end_time;
    $this->request->add(['end_at' => $end_at]);
}

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
            'start_at' => [
                'nullable',
                'date',
                'before_or_equal:end_at'
            ],
            'end_at' => [
                'nullable',
                'date',
                'after_or_equal:start_at'
            ],
        ];
}

validationDataはバリデーションされる前に呼ばれるメソッドなので、
これをオーバーライドして任意のパラメータをrequestパラメータに追加する。

フォームリクエストバリデーションなら、
$this->request->add()で任意のパラメータを増やせるので、バリデーション前に足す・・・ならギリありという判断。

パラメータは文字列で渡すので、dateフォーマットになってるか?とかバリエーション任せにできるし、
from - toの並びが正しいかもLaravel任せにできるのでコードもスッキリ。

これで_date、_timeのチェック不要になる。
ただ、_date、_timeでフォーマットが厳格に決まっている場合は、
それぞれちゃんとチェックして入力エラーがあれば画面上で判るようにしといてあげる、程度の配慮は必要です。

さいごに

複数フィールドに対するバリデーションの仕方(required_ifとか使う方法)は割とよく出てるけど、
複数フィールドを合体させて1つのフィールドとしてバリデーションする方法は、情報がないので書いてみました。

最近コードが載らない記事は全部はてなに書いてるので久しぶりにQiitaに投稿してみましたが、
何かプレビューモードが見やすくなったような・・・気のせいかも。

また、コードが載るものを書くときはQiitaに書きます(多分)

では~。