关于5.1版验证类中唯一(unique)规则的缺陷

浏览:3998 发布日期:2018/01/06 分类:ThinkPHP5专区 关键字: thinkphp5.1 5.1 unique
我们通常会有这样的应用场景,比如在操作用户表(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']不晓得我这样分析是否正确,请大牛批评指正,同时也希望官方给予回复。
最佳答案
评论( 相关
后面还有条评论,点击查看>>