Yii2实现唯一性校验有三种方式:一是用内置UniqueValidator配置字段、表及条件;二是自定义验证方法访问模型状态手动查库;三是封装独立验证器类复用复杂逻辑,同时需注意排除自身ID、添加数据库UNIQUE索引、处理NULL值及避免N+1查询。

Yii2 中实现自定义验证来校验“唯一性”参数,核心是结合 UniqueValidator 或自定义验证方法,并确保能动态传入校验字段、条件和模型上下文。下面分几种常用场景说明实现方式:
使用内置 UniqueValidator(推荐用于简单唯一校验)
在模型的 rules() 方法中直接配置,支持指定字段、目标表、条件等:
- 校验用户名在
user表中唯一:
['username', 'unique', 'targetClass' => User::class, 'message' => '该用户名已被注册'] - 校验邮箱在当前表中唯一,且仅对未删除用户生效:
['email', 'unique', 'filter' => ['!=', 'status', User::STATUS_DELETED']] - 跨字段联合唯一(如 username + tenant_id):
[['username', 'tenant_id'], 'unique', 'targetAttribute' => ['username', 'tenant_id']]
自定义验证方法(适合复杂逻辑或需访问当前模型状态)
在模型类中定义一个验证方法,通过 $this 访问当前属性值和其他字段,再手动查库判断:
- 在模型中添加方法:
public function validateEmailUniqueness($attribute, $params)
{
$query = User::find()->where(['email' => $this->email]);
if ($this->id) {
$query->andWhere(['!=', 'id', $this->id]);
}
if ($query->exists()) {
$this->addError($attribute, '该邮箱已被使用');
}
} - 在
rules()中绑定:
['email', 'validateEmailUniqueness']
封装成独立验证器类(适合多模型复用或含业务规则)
新建一个继承 Validator 的类,重写 validateAttribute(),可注入服务、调用 API 或执行复杂判断:
- 例如:校验邀请码是否有效且未被使用过
class InviteCodeValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
$code = $model->$attribute;
$used = InviteService::isUsed($code);
if ($used) {
$model->addError($attribute, '邀请码已失效或已被使用');
}
}
} - 在 rules 中使用:
['invite_code', InviteCodeValidator::class]
注意事项与避坑点
实际开发中容易忽略的关键细节:
-
更新场景必须排除自身 ID:否则编辑时会误判为重复;建议用
$this->isNewRecord判断新增/更新 - 数据库索引要配套:唯一性校验不能只靠 PHP 层,务必在对应字段加 UNIQUE 索引,防止并发插入冲突
-
关联字段注意空值处理:如
tenant_id为 NULL 时,MySQL 中多个 NULL 不触发唯一约束,需在验证逻辑中显式过滤 -
避免 N+1 查询:自定义方法里慎用
findOne(),优先用exists()提升性能











