PHP
Laravel

【Laravel】複数カラムのユニーク制約のバリデーションを実装する方法

More than 1 year has passed since last update.

公式サイトにもあるのですが、この辺5.3でやり方が増えたらしく、ググってるときにちょっとわかりづらかったので自分用にまとめ直しました。

自分は5.5で確認しています。


例. sampleテーブルのfirstカラムとsecondカラムでユニーク制約したときの書き方

use Illuminate\Validation\Rule;

/**
* Get the validation rules that apply to the request.
*
* @return array
*/

public function rules()
{
// firstカラムとsecondカラムのユニーク制約のバリデーション。どちらも必須とする
return [
'first' => 'required',
'second' => [
'required',
// Ruleクラスを使用した5.3からの記法。視覚的にわかりやすくなっている
// sampleテーブルでユニーク制約。ignoreで入力されたidはバリデーションから除外する
Rule::unique('sample')->ignore($this->input('id'))->where(function($query) {
// 入力されたfirstの値と同じ値を持つレコードでのみ検証する
$query->where('first', $this->input('first'));
}),
],
// 5.2までの記法。1行で書けるがあまり直感的ではない
// 'second' => 'required|unique:sample,second,' . $this->input('id'). ',id,first,' . $this->input('first'),
];
}


5.3からRuleクラスの記法が加わって、基本そっちの方がわかりやすいので使ったほうがよさげ

Ruleクラスをuseして使用する。

requiredなど別の制約を同時に課したい場合は、|を区切り文字にする代わりに配列記法で書く。


ignoreで更新する自身のレコードを無視する

複数カラムに限らずユニーク制約のバリデーションをするときに、更新時に自身のレコードの値まで内容に含めてしまってバリデーションに引っかかるのは望ましい挙動ではないはずなので外すことが多い。

id
first

1
A

2
B

3
C

どういうことかというと、上記のようなsampleテーブルがありfirstカラムにユニーク制約のバリデーションをかけるとして、idが1のレコードのfirstカラムをAとして更新し直すような場合にもバリデーションで引っかかってしまうのである。

テーブルとしてはすでにfirstカラムにAという値が存在しているのにそれと同じ値で更新するので、ユニーク制約に引っかかるということになる。

個人的にはこれぐらいデフォルトで気を利かせてほしい気もするのだけど。。

Rule::unique('sample')->ignore($this->input('id'));

で、このようにignoreメソッドに自身のレコードのidを渡すと、そのレコードの内容だけは無視して扱われるので、バリデーションに引っかからないようになる。

もし、idカラムが主キーでない場合は、第2引数に任意のカラム名を渡せる。


さらに複数カラムでのユニーク制約の場合、whereで参照するレコードを絞る

複数カラムでのユニーク制約の場合、さらに参照するレコードを絞ることで、複数カラムで一意ということを検証できるようにする。

id
first
second

1
A
Apple

2
A
Orange

3
B
Apple

4
B
Orange

5
C
Apple

上記のようなsampleテーブルがあり、firstとsecondの複数カラムでユニーク制約のバリデーションをかけるとする。

新規レコードとして、firstがC、secondがOrangeのデータを入れたいとしたら、複数カラムで一意であればよいので、 すでにテーブルにあるレコードとしてはfirstカラムがCのレコードだけを見て、それらのレコードの中にsecondがOrangeのものがなければよいことになる。

Rule::unique('sample')->ignore($this->input('id'))->where(function($query) {

// 入力されたfirstの値と同じ値を持つレコードでのみ検証する
$query->where('first', $this->input('first'));
}),

なのでSQLでいうと WHERE first = 'C' のようなクエリビルダを書いて、参照するレコードを絞る。


5.2までの記法なら1行でもかける

'unique:sample,second,' . $this->input('id'). ',id,first,' . $this->input('first'),

unique:テーブル,カラム,除外ID,IDカラム,where制約カラム,where制約カラムの値

もちろんイコール以外も否イコールとかIS NULLとかも書き方次第でいける。

それらの記法はこちらの記事が参考になる。

[Laravel]バリデーションのuniqueにおける条件指定について - Qiita


公式サイト

https://readouble.com/laravel/5.5/ja/validation.html