thinkphp validate unique 資料庫欄位唯一性校驗

big_cat發表於2022-06-10

當我們需要通過驗證器 validate 來對資料庫中的欄位做唯一性校驗時,可以通過 unique 規則實現,但 tp 並沒有很詳細的說明如何使用。

這裡結合我日常開發中的一些場景,給大家整理一下:

unique 語法規則

// modelClassName 你需要關聯校驗的模型類名(直接寫表名會比較硬編碼)
// col1^col2 參與唯一校驗的欄位
// [
//   ['col1', '=', $formData['col1']],
//   ['col2', '=', $formData['col2']],
// ]
// $pkId 「編輯」場景防自殺
// [['id', '<>', $pkId]]

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

模型

/**
 * @property int $id
 * @property string $name 唯一
 * @property int $age
 * @property int $deleted 軟刪除
class User extends Model
{
    protected $connection = "db_app";//多庫連結配置
}

校驗器基類

根據表單資料中是否有 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

相關文章