你们想的太复杂了,其实修改场景的规则不用重新写,重点是编辑界面一定要使用隐藏域传递主键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']
复制代码
不晓得我这样分析是否正确,请大牛批评指正,同时也希望官方给予回复。