1.关于模型自动验证和自动完成的心得:
(1)官方文档里验证规则为function的时候,我们可以使用匿名函数来实现文档中调用外部定义好函数的功能,如下:
$validate = array(
array('goods_id','require','请选择商品',Model::MUST_VALIDATE),
array('invite_limit','number','邀请好友数量格式错误',Model::MUST_VALIDATE,'regex'),
array('invite_limit',function($data){
return intval($data) < 1 ? false : true;
},'邀请好友数量必须大于0',Model::MUST_VALIDATE,'function')
);
这样的话,我们就可以不用在外面单独为这个验证规则定义一个专用的函数,也能使代码更简单易读.同理,这个匿名函数同样适用于自动完成的"function"的附加规则
(2)除了验证单个字段,我们还可以组合来验证,比如两个字段比大小,如下:
$validate = array(
array('goods_id','require','请选择商品',Model::MUST_VALIDATE),
array('seckill_start','require','请填写秒杀开始时间',Model::MUST_VALIDATE),
array('seckill_end','require','请填写秒杀结束时间',Model::MUST_VALIDATE),
array('seckill_start,seckill_end',function($data){
return strtotime($data['seckill_start']) >= strtotime($data['seckill_end']) ? false : true;
},'开始时间不能超过结束时间',Model::MUST_VALIDATE,'function'),
);
这个验证我们可以扒一扒源代码,首先找到核心文件的Model.class.php文件,找到create函数,因为验证需要create去触发,代码如下:
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', null, '__hash__');
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;
}
// 验证完成生成数据对象
if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
$fields = $this->getDbFields();
foreach ($data as $key=>$val){
if(!in_array($key,$fields)) {
unset($data[$key]);
}elseif(MAGIC_QUOTES_GPC && is_string($val)){
$data[$key] = stripslashes($val);
}
}
}
// 创建完成对数据进行自动处理
$this->autoOperation($data,$type);
// 赋值当前数据对象
$this->data = $data;
// 返回创建的数据以供其他调用
return $data;
}
通过$this->autoValidation找,一直找到_validationFieldItem函数,代码如下:
/**
* 根据验证因子验证字段
*
* @access protected
* @param array $data 创建数据
* @param array $val 验证因子
* @return boolean
*/
protected function _validationFieldItem($data,$val) {
switch(strtolower(trim($val[4]))) {
case 'function':// 使用函数进行验证
case 'callback':// 调用方法进行验证
$args = isset($val[6])?(array)$val[6]:array();
if(is_string($val[0]) && strpos($val[0], ','))
$val[0] = explode(',', $val[0]);
if(is_array($val[0])){
// 支持多个字段验证
foreach($val[0] as $field)
$_data[$field] = $data[$field];
array_unshift($args, $_data);
}else{
array_unshift($args, $data[$val[0]]);
}
if('function'==$val[4]) {
return call_user_func_array($val[1], $args);
}else{
return call_user_func_array(array(&$this, $val[1]), $args);
}
case 'confirm': // 验证两个字段是否相同
return $data[$val[0]] == $data[$val[1]];
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]];
}
$pk = $this->getPk();
if(!empty($data[$pk]) && is_string($pk)) { // 完善编辑的时候验证唯一
$map[$pk] = array('neq',$data[$pk]);
}
if($this->where($map)->find()) return false;
return true;
default: // 检查附加规则
return $this->check($data[$val[0]],$val[1],$val[4]);
}
}
分析这段源代码可以知,当验证规则数组的第一个值是数组或者以","相连的字符串,使用function或者callback附加规则时,会将需要验证的数据中的对应的值以一个参数传递给function/匿名函数或者类中的函数,这样的话只要我们按照这个规则去填写验证规则,我们就能实现更为复杂的验证,例如:
array('seckill_start,seckill_end',function($data){
return strtotime($data['seckill_start']) >= strtotime($data['seckill_end']) ? false : true;
},'开始时间不能超过结束时间',Model::MUST_VALIDATE,'function'),