让关联模型支持create自动验证

浏览:3960 发布日期:2014/05/16 分类:技术分享 关键字: 关联模型 create
最近在写代码时用到了关联模型,关联模型支持关联写入和关联更新,但关联表数据需要自己写,不能由create方法生成,因为在调用create方法时会把和主表无关的字段给过滤了。因自己的需要还是想在对表单进行验证时直接生成关联表的数据,于是对源代码做了部分改动。
大致思路如下:
在表单里主表及关联表的字段需要平级提交,在主表模型的create方法只做验证(关联表的字段也可在这里验证),不做字段的过滤,然后根据关联定义调用关联表模型,把属于关联表的字段分离出来并进行验证(如果有定义),以mappingName附加在主表上,最后再过滤掉非法的字段。
举例:
user和user_extend是一对一的关联关系,在表单里提交name、email、credit、balance、is_add五个字段, name和email属于user表,credit、balance属于user_extend表。执行$data = $User->relation(true)->create()
得到的数据$data的格式为array('name'=>值,'email'=>值,'extend'=>array('credit'=>值,'balance=>值))

代码:
Model类:    /**
     * 创建数据对象 但不保存到数据库
     * @access public
     * @param mixed $data 创建数据
     * @param string $type 状态
     * @return mixed
     */
     public function create($data='',$type='') {
        // 如果没有传值默认取POST数据
        if(empty($data)) {
            $data   =   I('post.');
        }elseif(is_object($data)){
            $data   =   get_object_vars($data);
        }
        // 验证数据
        if(empty($data) || !is_array($data)) {
            $this->error = L('_DATA_TYPE_INVALID_');
            return false;
        }

        // 状态
        $type = $type?:(!empty($data[$this->getPk()])?self::MODEL_UPDATE:self::MODEL_INSERT);

        // 检查字段映射
        if(!empty($this->_map)) {
            foreach ($this->_map as $key=>$val){
                if(isset($data[$key])) {
                    $data[$val] =   $data[$key];
                    unset($data[$key]);
                }
            }
        }

        // 检测提交字段的合法性
        if(isset($this->options['field'])) { // $this->field('field1,field2...')->create()
            $fields =   $this->options['field'];
            unset($this->options['field']);
        }elseif($type == self::MODEL_INSERT && isset($this->insertFields)) {
            $fields =   $this->insertFields;
        }elseif($type == self::MODEL_UPDATE && isset($this->updateFields)) {
            $fields =   $this->updateFields;
        }
        if(isset($fields)) {
            if(is_string($fields)) {
                $fields =   explode(',',$fields);
            }
            // 判断令牌验证字段
            if(C('TOKEN_ON'))   $fields[] = C('TOKEN_NAME');
            foreach ($data as $key=>$val){
                if(!in_array($key,$fields)) {
                    unset($data[$key]);
                }
            }
        }

        // 数据自动验证
        if(!$this->autoValidation($data,$type)) return false;

        // 表单令牌验证
        if(!$this->autoCheckToken($data)) {
            $this->error = L('_TOKEN_ERROR_');
            return false;
        }

        // 验证完成生成数据对象
        $fields =   $this->getDbFields();
        if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
            $diff = array_diff(array_keys($data),$fields);
            foreach ($diff as $key){
                unset($data[$key]);
            }
        }
        $intersect = array_intersect($fields,array_keys($data));
        foreach ($intersect as $key) {
            if(MAGIC_QUOTES_GPC && is_string($data[$key])){
                $data[$key] =   stripslashes($data[$key]);
            }
        }

        // 创建完成对数据进行自动处理
        $this->autoOperation($data,$type);
        $this->_after_create($data,$this->options); //回调
        // 赋值当前数据对象
        $this->data =   $data;
        // 返回创建的数据以供其他调用
        return $data;
     }
    // 创建数据对象后的回调方法
    protected function _after_create(&$data,$options) {}
改动地方为:        // 验证完成生成数据对象
        $fields =   $this->getDbFields();
        if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
            $diff = array_diff(array_keys($data),$fields);
            foreach ($diff as $key){
                unset($data[$key]);
            }
        }
        $intersect = array_intersect($fields,array_keys($data));
        foreach ($intersect as $key) {
            if(MAGIC_QUOTES_GPC && is_string($data[$key])){
                $data[$key] =   stripslashes($data[$key]);
            }
        }
新增了回调函数    // 创建数据对象后的回调方法
    protected function _after_create(&$data,$options) {}
在create方法的最后执行$this->_after_create($data,$this->options);进行回调

RelationModel类:
新增    // 表单验证成功后的回调方法
    protected function _after_create(&$data,$options) {
        // 获取关联数据 并附加到结果中
        if(!empty($options['link']))
            $this->crRelation($data,$options['link']);
    }
    /**
     * 关联数据验证
     * @access protected
     * @param mixed $data  数据对象
     * @param string $name 关联名称
     * @return mixed
     */
    protected function crRelation(&$data='',$name='') {
        if(empty($data) && !empty($this->data)){
            $data = $this->data;
        }elseif(!is_array($data)){
            // 数据无效返回
            return false;
        }
        if(!empty($this->_link)) {
            $fields =   $this->getDbFields();
            // 遍历关联定义
            foreach($this->_link as $key=>$val) {
                // 操作制定关联类型
                $mappingName =  $val['mapping_name']?$val['mapping_name']:$key; // 映射名称
                if(empty($name) || true === $name || $mappingName == $name || (is_array($name) && in_array($mappingName,$name)) ) {
                    // 操作制定的关联
                    $mappingType = !empty($val['mapping_type'])?$val['mapping_type']:$val;  //  关联类型
                    $mappingClass  = !empty($val['class_name'])?$val['class_name']:$key;            //  关联类名
                    $mappingKey =!empty($val['mapping_key'])? $val['mapping_key'] : $this->getPk(); // 关联键名
                   if(strtoupper($mappingClass)==strtoupper($this->name)) {                            
                        continue; // 自引用关联跳过
                    }
                    // 获取关联model对象
                    $model = D($mappingClass);
                    foreach ($model->getDbFields() as $field) {
                        if (array_key_exists($field, $data) && $field != $mappingKey) {
                            $_data[$field] = $data[$field];
                            unset($data[$field]);
                        }
                    }
                    if ($_data = $model->token(false)->create($_data)) {
                        $data[$mappingName] = $_data;
                        $fields[] = $mappingName;
                    } else {
                        $this->error = $model->getError();
                        return false;
                    }
                }
            }

            // 过滤非法字段数据
            $diff = array_diff(array_keys($data),$fields);
            foreach ($diff as $key){
                unset($data[$key]);
            }
        }
    }
改动了relation方法,增加了一句$this->autoCheckFields = false;     /**
     * 进行关联查询
     * @access public
     * @param mixed $name 关联名称
     * @return Model
     */
    public function relation($name) {
        $this->options['link']  =   $name;
        $this->autoCheckFields = false; //执行create方法时不过滤字段
        return $this;
    }

附件 Relation.rar ( 15.81 KB 下载:52 次 )

最佳答案
评论( 相关
后面还有条评论,点击查看>>