1. 程式人生 > >關於5.1版驗證類中唯一(unique)規則的缺陷

關於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,隨即我試著參照修改,

  1.      'edit' => ['nickname.unique'=>'require|unique:user,nickname^id']

複製程式碼

 

結果發現,他什麼都不驗證了,即使跟其他使用者的nickname重複,也通過了,沒辦法只有查核心庫原始碼:

  1.  /**
  2.      * 驗證是否唯一
  3.      * @access public
  4.      * @param  mixed     $value  欄位值
  5.      * @param  mixed     $rule  驗證規則 格式:資料表,欄位名,排除ID,主鍵名
  6.      * @param  array     $data  資料
  7.      * @param  string    $field  驗證欄位名
  8.      * @return bool
  9.      */
  10.     public function unique($value, $rule, $data, $field)
  11.     {
  12.         if (is_string($rule)) {
  13.             $rule = explode(',', $rule);
  14.         }
  15.         if (false !== strpos($rule[0], '\\')) {
  16.             // 指定模型類
  17.             $db = new $rule[0];
  18.         } else {
  19.             try {
  20.                 $db = Container::get('app')->model($rule[0]);
  21.             } catch (ClassNotFoundException $e) {
  22.                 $db = Db::name($rule[0]);
  23.             }
  24.         }
  25.         $key = isset($rule[1]) ? $rule[1] : $field;
  26.  
  27.         if (strpos($key, '^')) {
  28.             // 支援多個欄位驗證
  29.             $fields = explode('^', $key);
  30.             foreach ($fields as $key) {
  31.                 $map[] = [$key, '=', $data[$key]];
  32.             }
  33.         } else {
  34.             $map[] = [$key, '=', $data[$field]];
  35.         }
  36.  
  37.         $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
  38.  
  39.         if (is_string($pk)) {
  40.             if (isset($rule[2])) {
  41.                 $map[] = [$pk, '<>', $rule[2]];
  42.             } elseif (isset($data[$pk])) {
  43.                 $map[] = [$pk, '<>', $data[$pk]];
  44.             }
  45.         }
  46.         if ($db->where($map)->field($pk)->find()) {
  47.             return false;
  48.         }
  49.         return true;
  50.     }

複製程式碼

 

分析原始碼並打印出他最終生成的查詢條件:

  1. array(3) {
  2.   [0] => array(3) {
  3.     [0] => string(8) "nickname"
  4.     [1] => string(1) "="
  5.     [2] => string(4) "tes1"
  6.   }
  7.   [1] => array(3) {
  8.     [0] => string(2) "id"
  9.     [1] => string(1) "="
  10.     [2] => int(3)
  11.   }
  12.   [2] => array(3) {
  13.     [0] => string(2) "id"
  14.     [1] => string(2) "<>"
  15.     [2] => int(3)
  16.    }
  17. }

複製程式碼

 

返現關於id的條件是同時滿足id = 3 或 id<>3(3就是我的使用者id),顯然這個條件是自相矛盾的,所以等於對id沒有做任何過濾,那麼造成這個結果的原因就是這段程式碼:

  1.        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
  2.  
  3.         if (is_string($pk)) {
  4.             if (isset($rule[2])) {
  5.                 $map[] = [$pk, '<>', $rule[2]];
  6.             } elseif (isset($data[$pk])) {
  7.                 $map[] = [$pk, '<>', $data[$pk]];
  8.             }
  9.         }

複製程式碼

 

這裡就有個順序問題,他的順序是先值後欄位名,那麼很多情況是,我的值是通過$data傳遞進去的,所以值我是不設定的,而按照這個順序,我不設定值就沒法設定過濾欄位名,所以最佳的解決辦法就是把這兩個順序換下,把欄位名方前面,值放後面:

  1. $pk = !empty($rule[2]) ? $rule[2] : $db->getPk();
  2.  
  3.         if (is_string($pk)) {
  4.             if (isset($rule[3])) {
  5.                 $map[] = [$pk, '<>', $rule[3]];
  6.             } elseif (isset($data[$pk])) {
  7.                 $map[] = [$pk, '<>', $data[$pk]];
  8.             }
  9.         }

複製程式碼

 

經測試,發現新增和更新都能被正確驗證:更新時只要:

  1.      'edit' => ['nickname.unique'=>'require|unique:user, nickname, id']

複製程式碼

 

不曉得我這樣分析是否正確,請大牛批評指正,同時也希望官方給予回覆。

轉自:http://www.thinkphp.cn/topic/53965.html