unique规则校验字段唯一性,默认查指定表对应列并忽略软删除记录;更新时需用ignore排除自身,支持where添加多条件限定,须配合数据库唯一索引应对并发竞态。

验证字段在数据库中是否唯一(unique 规则基础用法)
直接用 unique 规则就能检查某字段在指定表里是否存在重复值。它默认校验当前字段名对应的列,比如字段叫 email,就查 users.email。
常见写法:
['email' => 'required|unique:users']
注意点:
- 表名必须小写,Laravel 不会自动转换驼峰或下划线命名的表名
- 如果模型使用了非默认连接(如
mysql2),需显式指定:unique:users,email,0,id,connection,mysql2 - 不加其他参数时,
unique会忽略软删除记录(即deleted_at IS NULL自动生效)
排除当前记录(更新场景下的 ignore 参数)
表单编辑时,提交的邮箱没变,但 unique:users 会把它当成新记录查重,导致“自己不能保存”。必须用 ignore 排除自身。
推荐写法(传入主键值):
['email' => 'required|unique:users,email,' . $user->id]
更安全的写法(避免 ID 被篡改):
['email' => ['required', Rule::unique('users', 'email')->ignore($user)]]
关键差异:
- 字符串写法中第三个参数是
id值,第四个参数才是id字段名(默认就是id,可省略) -
Rule::unique()方式能自动适配主键名(比如用uuid当主键也不用额外指定) - 若用字符串写法却漏掉
$user->id,更新时必报“已存在”错误
跨字段、多条件唯一性(where 条件组合)
有些业务要求“同一公司下邮箱唯一”,不能只查全局。这时得加查询条件,用 where 限定范围。
字符串写法(注意引号和逗号顺序):
'email' => 'unique:users,email,NULL,id,company_id,' . $companyId
更清晰的 Rule 写法:
Rule::unique('users', 'email')->where(fn ($q) => $q->where('company_id', $companyId))
容易踩的坑:
- 字符串方式里,
NULL是占位符,表示跳过主键排除逻辑;别误写成null或空字符串 - 多个
where条件要用链式调用,不能在一个where()里塞数组(那会变成WHERE IN) - 如果
$companyId可能为空,得提前判断,否则生成的 SQL 会查company_id = NULL(不是IS NULL)
自定义错误消息与中文提示
Laravel 默认的 The :attribute has already been taken. 对用户不友好。中文项目必须覆盖。
最简方案:在 resources/lang/zh/validation.php 中添加:
'unique' => ':attribute 已被占用。'
进阶控制(仅对某字段定制):
['email' => 'required|unique:users']
对应消息:
'email.unique' => '邮箱已被注册,请换一个'
注意:
- 键名必须严格匹配规则顺序,
email.unique比unique优先级高 - 如果用了
Rule::unique()但没写->message(...),仍走语言包配置 - 不要在验证规则里拼接中文(如
'unique:users|邮箱已被占用'),Laravel 不识别
唯一性验证真正难的不是写法,而是边界:软删除数据要不要参与校验、多租户下如何动态绑定租户 ID、并发写入时的竞态——这些靠 unique 规则本身解决不了,得结合数据库唯一索引 + 应用层重试或事务兜底。











