關於5.1版驗證類中唯一(unique)規則的缺陷
你們想的太複雜了,其實修改場景的規則不用重新寫,重點是編輯介面一定要使用隱藏域傳遞主鍵id就可以了,TP會自動識別是否需要判斷唯一性
你們想的太複雜了,其實修改場景的規則不用重新寫,重點是編輯介面一定要使用隱藏域傳遞主鍵id就可以了,TP會自動識別是否需要判斷唯一性
你們想的太複雜了,其實修改場景的規則不用重新寫,重點是編輯介面一定要使用隱藏域傳遞主鍵id就可以了,TP會自動識別是否需要判斷唯一性
importent * 3
form:http://www.thinkphp.cn/topic/47768.html
我們通常會有這樣的應用場景,比如在操作使用者表(User)時,我們希望username和nickname這兩個欄位是唯一的,所以在新增和更新時就要對他的唯一性做驗證,內建的unique規則在新增時驗證沒有任何問題,但是在更新時就會碰到如果提交資料時nickname沒有被修改(username通常一旦註冊不能修改),那麼驗證也會通不過,提示nickname已經存在。搜尋發現有人也碰到這個問題,傳送門:http://www.thinkphp.cn/topic/47768.html,隨即我試著參照修改,
'edit' => ['nickname.unique'=>'require|unique:user,nickname^id']
複製程式碼
結果發現,他什麼都不驗證了,即使跟其他使用者的nickname重複,也通過了,沒辦法只有查核心庫原始碼:
/**
* 驗證是否唯一
* @access public
* @param mixed $value 欄位值
* @param mixed $rule 驗證規則 格式:資料表,欄位名,排除ID,主鍵名
* @param array $data 資料
* @param string $field 驗證欄位名
* @return bool
*/
public function unique($value, $rule, $data, $field)
{
if (is_string($rule)) {
$rule = explode(',', $rule);
}
if (false !== strpos($rule[0], '\\')) {
// 指定模型類
$db = new $rule[0];
} else {
try {
$db = Container::get('app')->model($rule[0]);
} catch (ClassNotFoundException $e) {
$db = Db::name($rule[0]);
}
}
$key = isset($rule[1]) ? $rule[1] : $field;
if (strpos($key, '^')) {
// 支援多個欄位驗證
$fields = explode('^', $key);
foreach ($fields as $key) {
$map[] = [$key, '=', $data[$key]];
}
} else {
$map[] = [$key, '=', $data[$field]];
}
$pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
if (is_string($pk)) {
if (isset($rule[2])) {
$map[] = [$pk, '<>', $rule[2]];
} elseif (isset($data[$pk])) {
$map[] = [$pk, '<>', $data[$pk]];
}
}
if ($db->where($map)->field($pk)->find()) {
return false;
}
return true;
}
複製程式碼
分析原始碼並打印出他最終生成的查詢條件:
array(3) {
[0] => array(3) {
[0] => string(8) "nickname"
[1] => string(1) "="
[2] => string(4) "tes1"
}
[1] => array(3) {
[0] => string(2) "id"
[1] => string(1) "="
[2] => int(3)
}
[2] => array(3) {
[0] => string(2) "id"
[1] => string(2) "<>"
[2] => int(3)
}
}
複製程式碼
返現關於id的條件是同時滿足id = 3 或 id<>3(3就是我的使用者id),顯然這個條件是自相矛盾的,所以等於對id沒有做任何過濾,那麼造成這個結果的原因就是這段程式碼:
$pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
if (is_string($pk)) {
if (isset($rule[2])) {
$map[] = [$pk, '<>', $rule[2]];
} elseif (isset($data[$pk])) {
$map[] = [$pk, '<>', $data[$pk]];
}
}
複製程式碼
這裡就有個順序問題,他的順序是先值後欄位名,那麼很多情況是,我的值是通過$data傳遞進去的,所以值我是不設定的,而按照這個順序,我不設定值就沒法設定過濾欄位名,所以最佳的解決辦法就是把這兩個順序換下,把欄位名方前面,值放後面:
$pk = !empty($rule[2]) ? $rule[2] : $db->getPk();
if (is_string($pk)) {
if (isset($rule[3])) {
$map[] = [$pk, '<>', $rule[3]];
} elseif (isset($data[$pk])) {
$map[] = [$pk, '<>', $data[$pk]];
}
}
複製程式碼
經測試,發現新增和更新都能被正確驗證:更新時只要:
'edit' => ['nickname.unique'=>'require|unique:user, nickname, id']
複製程式碼
不曉得我這樣分析是否正確,請大牛批評指正,同時也希望官方給予回覆。