はじめに
こんにちは。今回はLaravelでバリデーションを書く時の、ややレアケースな「○○したい場合どうすればいいんだろう?」を備忘録的にまとめて紹介します。
基本的なバリデーションルールの設定方法は公式ドキュメントが分かりやすく、かつ充実してますので、それを読めば充分かと思います。
環境はPHP7.3.3, Laravel5.8です。
配列のバリデーションでRule::uniqueを使う時のカラム名を指定したい
1 2 3 | <input type="email" name="mail_address[]"> <input type="email" name="mail_address[]"> <input type="email" name="mail_address[]"> |
上記のようなhtmlの場合、
mail_address.*
と書くことで、配列の要素それぞれにバリデーションを実施できますが、下記のようにRule::uniqueを使ってユニーク確認をする時、生成されるSQL文のWHERE句が
WHERE mail_address.1 = '***@test.com'
のようになってしまい、そのまま実行すれば当然Exceptionが発生します。
1 2 3 4 | 'mail_address.*' => ['required', 'email', 'string', Rule::unique('users')->where(function (Builder $query) { $query->where('name', $this->input('name')); })] |
これを防いで
WHERE users.mail_address = '***@test.com'
のようなWHERE句を生成するには、Rule::unique()に第2引数としてカラム名を指定します。
1 2 3 4 | 'mail_address.*' => ['required', 'email', 'string', Rule::unique('users', 'mail_address')->where(function (Builder $query) { $query->where('name', $this->input('name')); })] |
配列バリデーションのエラーメッセージ制御
日本語のエラーメッセージが
resources/lang/ja/validation.php
に定義されている前提とします。
FormRequestのrules()を
'mail_address.*' => ['required', 'email', 'string']
としている場合、エラー時は以下のようなメッセージが出てしまいます。
- mail_address.0は必須です。
- mail_address.1には正しい形式のメールアドレスを指定してください。
- mail_address.2には文字列を指定してください。
attributes
でプレースホルダを定義することで属性名を日本語に置き換えることができますが、ここでもバリデーションルールと同様の書き方ができます。下記は1次元配列ですが、多次元配列でも置換可能です。
1 2 3 4 | 'attributes' => [ 'mail_address.*' => 'メールアドレス' // 配列の何番目であっても「メールアドレス」に置換される 'mail_address.0' => 'メールアドレス[1]' // 配列の1番目だけ置換される ] |
カスタムバリデーションで、他の属性名を参照したい
独自バリデーションの作り方はいくつかありますが、
ServiceProvider
で登録する方法の場合です。
validate****
のようなメソッド名で独自のバリデーションを実装する際、このメソッド内で別の属性を参照したくなるケースもあるかと思います。2つの入力値の組み合わせで何か判定したいとか。
1 2 3 4 5 6 7 8 9 | public function rules() { return [ ... 'name' => ['required', 'string', 'max:20'], 'mail_address' => ['required', 'hoge'], ... ]; } |
1 2 3 4 5 6 7 8 9 10 11 | namespace App\Validator; class CustomValidation extends \Illuminate\Validation\Validator { public function validateHoge($attribute, $input, $parameters) { ... ... return $isValid; } } |
上記の
validateHoge
では
$attribute
で属性名、
$input
で入力値を受け取れますが、例えば
name
の値が欲しくなったら、継承元のValidatorクラスの
getValue()
で取得できます(引数は属性名)。
1 2 3 4 | protected function getValue($attribute) { return Arr::get($this->data, $attribute); } |
番外:複数の1次元配列を結合して2次元配列にしたい
何それ?だと思いますので下記コードをご覧ください。日付の1次元配列と時間の1次元配列を結合して、日時がセットになってる2次元配列を生成しています。
標準関数のarray_map一発でできてびっくりしました。自力で実装するならforeachをネストしてゴリゴリ書くことになると思います。
1 2 3 4 | $dateList = ['2017-04-01', '2018-05-02', '2019-06-03']; $timeList = ['05:00:00', '14:30:20', '22:08:05']; $dateTimeList = array_map(null, $dateList, $timeList); print_r($dateTimeList); |
結果、以下のようになります。結合する配列の長さが同じであることが前提にはなりますが、すごい。10年近くPHP書いてますがこんなの知らなかった…。
Laravelとは関係無いtipsですが、先日PHPの配列操作であれこれ調べてる時に見つけて、ちょっと衝撃だったので紹介しました。
このサンプルでは2つの配列ですが、3つ以上でも動作します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Array ( [0] => Array ( [0] => '2017-04-01' [1] => '05:00:00' ) [1] => Array ( [0] => '2018-05-02' [1] => '14:30:20' ) [2] => Array ( [0] => '2019-06-03' [1] => '22:08:05' ) ) |
さいごに
Laravel、かゆいところに手が届いて楽しいです。「○○したいな」と思って調べれば大抵書き方が用意されていますので、よくできてるな〜と感じること頻りです。