Laravelのバリデーションルールには、after, before, after_or_equal, before_or_equal がある。 これらのルールは、あるフィールドの日付が別のフィールドの日付と比較して「後/前」になっているかをチェックができます。
問題の概要
- ルールの動作
'start_at' => 'nullable|date|before_or_equal:end_at',
'end_at' => 'nullable|date|after_or_equal:start_at',
- この設定は「start_at は end_at と同じかそれより前の日付であること」、「end_at は start_at と同じかそれより後の日付であること」をvalidationします
片方がnullの場合の問題
にも記載しているようにafter,beforeはstrtotimeに値を渡す関係上、どちらかがnullの場合はstrtotimeに渡したとき有効な日付として変換できず、結果的にバリデーションが失敗する仕様になる
解決方法
1. ルールの適用を片方にする
'start_at' => 'nullable|date',
'end_at' => 'nullable|date|after_or_equal:start_at',
2. カスタムバリデーションルールを使用する
- 匿名関数を用いて、両方のフィールドが存在する場合のみ比較を行うように設定する
'start_at' => [
'nullable',
'date',
function ($attribute, $value, $fail) {
// end_atが存在するときだけ比較
if ($value !== null && $this->end_at !== null) {
if (Carbon::parse($value)->greaterThan(Carbon::parse($this->end_at))) {
$fail("start_atには、end_at以前の日付を指定してください。");
}
}
},
],
'end_at' => [
'nullable',
'date',
function ($attribute, $value, $fail) {
// start_atが存在するときだけ比較
if ($value !== null && $this->start_at !== null) {
if (Carbon::parse($value)->lessThan(Carbon::parse($this->start_at))) {
$fail("end_atには、start_at以降の日付を指定してください。");
}
}
},
],
この方法は、両方のフィールドが存在する場合にのみ比較を実施する。片方がnullの場合は比較処理をスキップする。
まとめ
- 比較ルールは両方のフィールドに有効な日付があることを前提としているため、片方がnullだとバリデーションは失敗する
- 両方が存在する場合のみ比較するようカスタムバリデーションルールを用いる方法が良さそう