当我们需要通过验证器 validate 来对数据库中的字段做唯一性校验时,可以通过 unique 规则实现,但 tp 并没有很详细的说明及示例。

同时 unique 验证器需要对 除当前活动记录 外的数据做唯一性校验时,没有做相关封装,只能在业务层面即时硬写规则,这样不够优雅。所谓验证规则,就应该在验证器中。

这里结合我日常开发中的一些场景,给大家整理一下:

unique 语法规则

// modelClassName 你需要关联校验的模型类名(直接写表名会比较硬编码)
// col1^col2 参与唯一校验的字段
// [
//   ['col1', '=', $formData['col1']],
//   ['col2', '=', $formData['col2']],
// ]
// $pkId 「编辑」场景防自杀
// [['id', '<>', $pkId]]

protectd $rule = [
    "colName" => "require|unique:modelClassName,col1[^col2][,$pkId]"
]

// 给出具体规则所组装出的唯一性查询语句
// "require|unique:User,name^deleted"
$exists = $query->where([
  'name' => $formData['name'],
  'deleted' => $formData['deleted']
])->find();

// "require|unique:User,name^deleted, 1"
$exists = $query->where([
  'name' => $formData['name'],
  'deleted' => $formData['deleted']
])->where([['id', '<>', 1]])->find();

模型

/**
 * @property int $id
 * @property string $name 唯一
 * @property int $age
 * @property int $deleted 软删除
class User extends Model
{
    protected $connection = "db_app";//多库链接配置
}

验证器基类-优化ed

根据 表单数据 中是否有 pkId 来动态的生成 unique 的校验规则

<?php

namespace app\common\validate;

use think\Validate;

/**
 * Class MaterialTypeValidate
 *
 * @package app\prt\validate
 * @see     \think\Validate::unique 解析器
 */
class BaseValidate extends Validate
{
    /**
     * 根据表单数据动态挂载 pkId
     * 编辑时的 unique 校验 应使用 pkId 来忽略当前行
     * 同时如果有软删除 那么还要加入 deleted=0 的条件来限制只校验有效记录
     * formData ['col1' => xxx, 'col2' => xxx, 'deleted'=>0]
     * unique|modelName,col1^col2^deleted,pkId
     * unique多字段校验时验证器会检查表单数据有对应的字段,存在才会加入条件
     * 为了忽略软删除要让表单数据中的 deleted=0 显示定义,排除已删除的数据
     * BaseValidate constructor.
     *
     * @param int   $pkId
     * @param array $rules
     * @param array $message
     * @param array $field
     */
    public function __construct($pkId = 0, array $rules = [], array $message = [], array $field = [])
    {
        parent::__construct($rules, $message, $field);
        // 有 id 则为编辑,追加pkId条件
        // 无 id 则为新增,全局唯一检查
        array_walk($this->rule, function (&$row) use ($pkId) {
            $row = str_replace(",{pkId}", "," . $pkId ?? "", $row);
        }, $this->rule);
    }
}

新增时需要校验全局有效记录的字段唯一性,编辑 时需要校验 除当前记录外 的字段唯一性。

<?php

namespace app\http\validate;

use app\common\validate\BaseValidate;
use app\http\model\User;

/**
 * Class UserValidate
 *
 * @package app\http\validate
 */
class UserValidate extends BaseValidate
{
    protected $rule = [
        'id'     => 'require|number',
        'name'   => 'require|unique:' . User::class . ',name^deleted,{pkId}',
        'age' => 'require|number',
    ];

    protected $message = [
        'id.require'   => 'id无效',
        'id.number'    => 'id无效',
        'name.require' => '姓名必须',
        'name.unique'  => '姓名重复',
        'age.require'  => '年龄必须',
        'age.number'   => '年龄无效',
    ];
    
    protected $scene = [
        'add'  => ['name', 'age'],
        'edit' => ['id', 'name', 'age'],
    ];
}

实例

public function save($userData)
{
    // 根据有无 id 来动态渲染 unique 条件的校验范围
    $validate = new UserValidate($userData['id'] ?? 0);
    
    // 只校验未删除的有效数据
    if (User::hasSoftDelete()) {
        $userData['deleted'] = 0;
    }

    if (empty($userData['id'])) {
        $validateRes = $validate->scene('add')->check($userData);
    } else {
        $validateRes = $validate->scene('edit')->check($userData);
    }
    
    if (!$validateRes) {
        throw new Exception($validate->getError());
    }
}

unique 的解析器

\think\Validate::unique

big_cat
1.7k 声望130 粉丝

规范至上


引用和评论

0 条评论