ThinkPHP自动验证:unique规则的问题和理解

发布于:2013-09-28 | 分类:web development


本文针对ThinkPHP自动验证部分学习过程中遇到的几个问题,经过查阅文档及思考理解,作了以下几个方面的总结记录:

  1. 自动验证的实施

  2. unique验证规则的问题

  3. 验证信息的Ajax返回

自动验证的实施

自动验证是通过对表单提交的数据(可以是对应数据库表中的某些字段,也可以不是表中字段)设定一些验证规则,在create()操作时会根据此规则进行验证并给出相应信息,当然也可以进行手动验证。它包含两种方式1 2

  • 静态方式:在模型类里面通过$_validate属性定义验证规则。

  • 动态方式:使用模型类的validate方法动态创建自动验证规则。

使用格式:

array(
    array(验证字段1,验证规则,错误提示,[验证条件,附加规则,验证时间]),
    array(验证字段2,验证规则,错误提示,[验证条件,附加规则,验证时间]),
....);

unique自动验证规则的问题

根据第一部分的学习,已经可以建立如下规则,表示在插入数据时,验证username是否唯一。

array('username',' ','用户名已经存在!',0,'unique',1)

然而在学习初期可能遇到如下问题。

修改信息的时候,提示用户名已经存在

这显然是验证时机的混淆,我们根据上下文很容易判断需要进行的是编辑更新操作,可是程序如何判断?

程序是根据 主键值是否存在 来判定所要进行的操作的。例如,对于插入操作来说,主键值显然不存在;而编辑操作时,主键已经在之前的插入操作中存在了。所以解决这个问题的关键在于让程序知道目前操作是插入还是编辑。方法有两个:

  • 基本方法:根据以上基本原理,编辑操作时,在表单的隐藏域中给出待编辑的记录的主键值,这样程序通过搜索数据库就可以知道这条记录已经存在,即是编辑操作。

  • 直接方法:在调用create()函数时明确指明验证时机:create($data,2)

插入信息时,username明明没有重复而提示用户名已经存在

这个问题根源在于此时恰好将username设为主键了。解决方法很简单,重新设计主键即可,例如一个自动增长的id。其实这个在ThinkPHP文档中也有一点说明:

ThinkPHP的默认约定每个数据表的主键名采用统一的id作为标识,并且是自动增长类型的。系统会自动识别当前操作的数据表的字段信息和主键名称,所以即使你的主键不是id,也无需进行额外的设置,系统会自动识别3

事实上,假如设置了一个与数据表内容有直接关系的主键,例如这里的username,那么在对username的插入操作时就会出现问题2——username明明是唯一的却提示重复了。引起这一切的根源在于unique验证规则本身的设计,参照ThinkPHP开发包Core文件夹下Model.class.php文件关于unique的代码:

case 'unique': // 验证某个值是否唯一
    if(is_string($val[0]) && strpos($val[0],','))
        $val[0]  =  explode(',',$val[0]);
    $map = array();
    if(is_array($val[0])) {
        // 支持多个字段验证
        foreach ($val[0] as $field)
            $map[$field]   =  $data[$field];
    }else{
        $map[$val[0]] = $data[$val[0]];
    }
    if(!empty($data[$this->getPk()])) { // 完善编辑的时候验证唯一
        $map[$this->getPk()] = array('neq',$data[$this->getPk()]);
    }
    if($this->where($map)->find())   return false;
    return true;

从这里可以看出unique的逻辑是,查找数据库中有没有与验证数据重复的记录,查询条件有两个并且是and的关系:

  • 规则给出的字段、表单提交的数据

    $map[$val[0]] = $data[$val[0]]
    
  • 主键若存在,则要求表单提交的字段值与主键不相等

    $map[$this->getPk()] = array('neq',$data[$this->getPk()])
    

这样就可以理解上面两个问题了:

  • 问题1的第一个解决方法的道理在于,表单隐藏域中给出了主键值,则查询条件2保证了不会有重复记录。

  • 对于问题2,若要插入的username恰好是主键,则查询条件2覆盖了条件1,而此时的条件是表单提交的主键值在数据库中不存在,这显然是满足的,所以提示重复了。

验证信息的Ajax返回

自动验证结合Ajax返回可以无刷新给出相关信息,ThinkPHP提供了ajaxReturn()函数4

$this->ajaxReturn(返回数据,提示信息,操作状态)

下面通过create()函数自动验证并将getError()得到错误信息通过ajaxReturn()返回。错误信息为数组形式,键为验证字段,值为相应的验证信息。

if (!$m->create()){ // 如果创建失败 表示验证没有通过 输出错误提示信息
    $this->ajaxReturn('',$m->getError(),0);
}

前端Ajax请求及获取数据的Jquery代码:

$(document).ready(function(){
    $("input[type='button']").click(function(){
        $.post("adduser",$('form').serialize(),function(data){  // $('form').serialize()提交表单数据
            var json = eval('('+data+')'); // 得到Json对象
            // 输入不合法
            if(json.status == 0){ // to do
            }
            // 写入数据库错误
            else if(json.status == 1){ // to do
            }
            // 成功新建
            else if(json.status == 2){ // to do
            }
        });
    });
})