(1)创建数据对象
数据对象就是父类模型中的$this->data,AR模式的底层数据操作用到了数据对象。模型实例化之前数据对象只是空数组,后来使用了魔术方法__set设置了数据对象的值。
上述流程可以得出,使用数据对象前必须先创建数据对象。__set是设置数据对象的一种方法,但是应用时不便,设置一个属性就得设置一行代码。
因此ThinkPHP中系统封装了批量设置数据对象的方法:create方法
语法:$model->create();
关于参数,这里去分析父类模型
public function create($data='',$type='') { // 如果没有传值,默认取POST数据 if(empty($data)) { $data = I('post.'); }elseif(is_object($data)){ $data = get_object_vars($data); } }
通过实现代码发现,若不给create方法传参,则默认使用post中的数据。在结尾,将处理完成的data数据赋值给了data属性,这步便是创建数据对象;将处理完成的数据返回出去,
谁调用返回给谁。
案例:改写之前编写的部门信息入库的代码,使用数据对象的创建方式。
//判断请求类型,若是post则提交,否则展示 if (IS_POST) { //处理表单请求 //成功接收后,写入数据 $model = M('dept');//模型实例化 // echo $_POST['name']; $data = $model->create();// 数据对象的创建需要模型去调用,所以必须放在实例化模型后(不传递参数,接收post数据) // dump($data);die; $result = $model->add(); //判断返回值 if ($result) { $this->success('添加成功',U('showList'),3); }else{ $this->error('添加失败');//默认跳到上一页 } }else{...}
注意:如表单中字段和数据表的字段不匹配,则创建数据对象时会被过滤掉。
如果想用打印方法查看数据是否正确,可以接收返回值。如果不想打印则可以不接受,在CURD操作时不需要给具体的操作方法传递参数。除非在使用自动验证时,必须接收
返回值。
(2)自动验证
①自定义规则:
在提交数据时,系统按照指定规则进行数据的有效性和合理性验证。系统不知道表单的具体情况,所以没有想过规则,这也就需要我们自己去指定规则。
如果需要使用自动验证,规则需要自己去定义。
②验证:
在前端的JS里的验证叫前端验证,在ThinkPHP里验证机制为后端 / 服务器验证。两者区别,因为js是在客户端的验证,所以可以完成无刷新的验证。而PHP是服务器端验证,
伴有刷新。除非用AJAX。
③语法:没有语法。。。由数据对象创建方法create方法去实现自动验证,这个在下面列出,我们需要做的就是指定相关验证规则。
Model.class.php
// 数据自动验证 if(!$this->autoValidation($data,$type)) return false;
所以综上可以得出,若想用自动验证则必须使用数据对象创建方法--create方法接收数据
④定义规则:
在父类模型里存在成员属性_validate,这个属性是二维数组格式,用来保存验证规则
// 查询表达式参数 protected $_validate = array(); // 自动验证定义
由于不能在父类模型里直接更改属性,所以这里可以把属性赋值到子类自定义模型中去定义规则。
数据验证可以进行数据类型、业务规则、安全判断等方面的验证操作。
数据验证有两种方式:
- 静态方式:在模型类里面通过$_validate属性定义验证规则。
- 动态方式:使用模型类的validate方法动态创建自动验证规则。
无论是什么方式,验证规则的定义是统一的规则,定义格式为:
array(
array(验证字段1,验证规则,错误提示,[验证条件,附加规则,验证时间]),
array(验证字段2,验证规则,错误提示,[验证条件,附加规则,验证时间]),
......
);
必选参数:①验证字段:表单里每一个表单项的name值;
②验证规则:针对验证字段的格式限制。require必须、email邮箱、number数字、URL地址、currentcy货币
③错误提示:验证失败后的提示信息
可选参数:①验证条件:0默认,字段存在就验证;1必须验证;2字段不为空时验证。
②附加规则:结合验证规则配合使用,具体方法可以参考手册
③验证时间:分3种。新增数据时验证;编辑数据时验证;两者都验证
(3)案例:针对部门添加功能,使用自动验证来验证字段合法性
DempModel.class.php:
// 自动验证定义 protected $_validate=array( //规则编写,参考手册 // 1.针对部门名称规则 array('name','require','部门名称不能为空'),//必填 array('name','','部门名称已经存',0,'unique'),//不重复,这步会走数据库 //2.针对排序验证 array('sort','number','排序必须为数字'),//是否为数字,还可以使用函数function来验证 // array('sort','is_numeric','排序必须是数字',0,'function');//函数名要求是函数库的或当前模型定义声明的 array('remark','require','必须添加备注'),//备注必填 );
注意:因为这里是定义在了自定义模型里,所以实例化模型时需要实例化自定义模型。控制器DempController.class.php里修改
$model = D('dept');//模型实例化
①验证失败:
此时提交空表单,输出返回值dump($data);die;浏览器输出false。为什么是false?这里我们可以看父类模型,因为这里返回false
// 数据自动验证 if(!$this->autoValidation($data,$type)) return false;
②验证成功:如果验证成功,则返回正常数组
输出用户提示信息:$model->getError();
$model = D('dept');//模型实例化 // echo $_POST['name']; $data = $model->create();// 数据对象的创建需要模型去调用,所以必须放在实例化模型后(不传递参数,接收post数据) if (!$data) { // echo $model->getError(); $this -> error($model->getError());exit;//虽然理论上回跳回上一页,但PHP底层代码会继续执行。所以必须加exit }
(4)批量验证
一次性验证全部字段,需要配置成员属性
系统支持数据的批量验证功能,只需要在模型类里面设置patchValidate属性为true( 默认为false),
protected $patchValidate = true;
设置批处理验证后,getError()
方法返回的错误信息是一个数组,返回格式是:
array("字段名1"=>"错误提示1","字段名2"=>"错误提示2"... )
前端可以根据需要需要自行处理,例如转换成json格式返回:
$User = D("User"); // 实例化User对象 if (!$User->create()){ // 如果创建失败 表示验证没有通过 输出错误提示信息 $this->ajaxReturn($User->getError()); }else{ // 验证通过 可以进行其他数据操作 }
(3)字段映射
①映射表示对应关系
②应用场景:目前表单中的name值和数据表中的字段名一致,有一些人可能通过当前的功能和表单的name值猜测出表名字和表结构。后期可能会找到系统的漏洞,对系统进行
攻击。系统的安全性存在威胁。因此可以使用类似障眼法,将name值随机指定,这时name值和表字段不一致,那样也就猜测不出表结构了。
如果字段和数据表中的字段不匹配,会被系统过滤。所以需要有个对照列表,告知系统,不对应的name值是数据表的字段,防止字段被过滤。这时便用到了字段映射
③语法:没有语法、、、、只有规则定义$_map,与自动验证一样,在父类模型里有过定义
// 查询表达式参数 protected $_validate = array(); // 自动验证定义 protected $_map = array(); // 字段映射定义
将其复制到自定义子模型定义,
// 字段映射定义
protected $_map=array(
//映射规则,键是表单中的name值 = 值是表单中的字段名
'abcde'=>'pid',
'fghij'=>'sort'
);
因为当前映射是建立在父类模型create方法里,所以在控制器里仍然需要create方式进行数据对象的创建
此时前端表单中替换name值,任然可以运行
pid:<input type="text" name="abcde"><br/> sort:<input type="text" name="fghij"><br/>
在使用字段映射后,被映射的字段会被放到数组最后。按照字段映射的先后顺序进行排列。
(4)特殊表的实例化操作
表没有前缀,或者表前缀不是配置文件里定义的前缀,这些都是特殊表
①新建一张特殊数据表szphp
②创建模型文件szphpModel.class.php
③szphp的实例化操作,D和M。按照之前操作实例化验证输出后报错:Table 'db_oa.sp_szphp' doesn't exist [ SQL语句 ] : SHOW COLUMNS FROM `sp_szphp`
实例化时会将前缀和表名自动连接起来,所以会报错提示sp_szphp表不存在。
解决办法:看下父类模型文件关于数据表名的定义
// 数据表名(不包含表前缀) protected $tableName = ''; // 实际数据表名(包含表前缀) protected $trueTableName = '';//告知系统当前模型所关联的表的真实表名,且是已经包含前缀的表名
这里我们选第二个,告诉ThinkPHP,这张表名已经包含前缀了,不要再加前缀。
通过父类模型属性中的成员属性trueTableName属性,进行表名的指定。告知模型表名真实名字,让其不要再关联上前缀。
输出$model结果
["model":protected] => string(5) "szphp"
["queryStr":protected] => string(25) "SHOW COLUMNS FROM `szphp`"//没有加前缀
["modelSql":protected] => array(1) {
["szphp"] => string(25) "SHOW COLUMNS FROM `szphp`"
}