对于任何应用程序,数据校验都是重要部分,因为它有且于确保模型中的数据遵守了应用程序的业务规则。 例如,你可能想要确保密码最少要有8位,或者确保用户名唯一。 定义校验规则使表单处理非常非常简单。
校验过程有许多不同的面。本节覆盖的是其中模型这一面。 即:在调用模型中的 save() 方法时发生了什么。 关于如何处理校验错误的显示的更多信息,参见: 表单助手。
数据校验的第一步是在模型中建立校验规则。这是用模型定义中的 Model::validate 数组实现的:
class User extends AppModel {
public $validate = array();
}
在上面的示例中,$validate 数组被添加到 User 模型中,但数组不包含校验规则。 假设 users 表有 login、password、email 和 born 列,下面的示例展示了应用在这些列上的一些简单的校验规则:
class User extends AppModel {
public $validate = array(
'login' => 'alphaNumeric',
'email' => 'email',
'born' => 'date'
);
}
上例展示了如何向模型列添加校验规则。对于 login 列,只接受字母和数字,email 必须是有效的邮件地址,born 必须是有效的日期。 如果提交的数据违反了定义的规则,这些校验规则定义能使 CakePHP 自动在表单中显示错误信息。
CakePHP 有许多校验规则,且易于使用。 一些内置的规则允许你校验 email、URL 和 信用卡数字的格式 - 我们稍后会讲到它们的细节。
下面是一个非常复杂的校验的示例,利用了一些内置的校验规则:
class User extends AppModel {
public $validate = array(
'login' => array(
'alphaNumeric' => array(
'rule' => 'alphaNumeric',
'required' => true,
'message' => 'Alphabets and numbers only'
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => 'Between 5 to 15 characters'
)
),
'password' => array(
'rule' => array('minLength', '8'),
'message' => 'Minimum 8 characters long'
),
'email' => 'email',
'born' => array(
'rule' => 'date',
'message' => 'Enter a valid date',
'allowEmpty' => true
)
);
}
其中两条是为 login 定义的:它只能包含字母和数字,并且长度必须在5至15之间。 password 列必须不少于8位长。 email 必须是有效的邮件地址,并且 born 必须是有效的日期。 还要注意的是,怎样定义校验失败时显示特定的错误信息。
上面的例子中,单个列可以使用多个校验规则。如果内置的规则不能满足你的要求,你可以添加自己需要的校验规则。
现在你已经看到了数据校验的全景,让我们瞧瞧如何在模型中定义这些规则。有三种不同的方法:简单数组、每个列的单个规则、每个列的多个规则。
顾名思义,这是定义校验规则最简单的方法。 这种方法定义规则是标准语法是:
public $validate = array('fieldName' => 'ruleName');
‘fieldName’ 是规则所适用的列的名字,‘ruleName’是预定义的规则名,例如 ‘alphaNumeric’、’email’ 或者 ‘isUnique’。
例如,要确保用户所提供的是格式正确的邮件地址,可以使用如下规则:
public $validate = array('user_email' => 'email');
这种定义手法对校验规则的工作有更好的控制。在我们讨论这个之前,先来看看向单个列添加一条规则的标准用法:
public $validate = array(
'fieldName1' => array(
'rule' => 'ruleName', // or: array('ruleName', 'param1', 'param2' ...)
'required' => true,
'allowEmpty' => false,
'on' => 'create', // or: 'update'
'message' => 'Your Error Message'
)
);
‘rule’ 键是必须的。如果仅设置了 ‘required’ => true,表单验证将无法正确工作。因为 ‘required’ 不是实际的规则。
正如你看到的,每个列(上面只演示了一个列)与包含了如下五个键的数组关联:‘rule’、 ‘required’、 ‘allowEmpty’、 ‘on’ 和 ‘message’。让我们仔细地观察这几个键。
‘rule’ 方法定义了校验方面并且指定了一个值或者一个数组。这个特定的 ‘rule’ 可能是模型中的一个方法的名字,核心 Validation 类的一个方法的名字,或者正则表达式。要了解关于默认规则的更多信息,请参见 内核核验规则。
如果 rule 不包含任何参数,’rule’ 可以是单个值,例如:
public $validate = array(
'login' => array(
'rule' => 'alphaNumeric'
)
);
如果 rule 包含参数(例如 max,min 或者范围),’rule’ 将是一个数组:
public $validate = array(
'password' => array(
'rule' => array('minLength', 8)
)
);
记住,用数组方式定义规则时,’rule’ 键是必须的。
这个键接受一个逻辑值、create 或者 update。将其设置为 true 将使这一列总是被必须的。设置为 create 或者update 将使这一列只在更新或创建操作时必须。如果 ‘required’ 等于 true,数组中必须提供此列。例如,如果定义如下校验规则:
public $validate = array(
'login' => array(
'rule' => 'alphaNumeric',
'required' => true
)
);
传递给模型的 save() 方法的数据必须包含提供给 login 列的数据。如果它不存在,那么校验失败。这个键的默认值为逻辑 false。
required => true 与 校验规则中的 notEmpty() 不是一回事儿。required => true 表示这个数组 键 必须提供 - 不意味着它必须有值。如果数据集没有提供,校验失败,但是如果提交了空值(’‘)是有可能成功的(这依赖于规则的详细定义)。
在 2.1 版更改: 添加了对 create 和 update 的支持。
如果设置为 false,这个列的值必须是 非空的,, the field value must be nonempty, 其中 “nonempty” 非空的定义为 !empty($value) || is_numeric($value)。 对于数值检测,当 $value 为0时,CakePHP 认为是正确的。
required 与 allowEmpty 的不同可能会造成混乱。'required' => true 意味着 $this->data 中不提供带有这个列的键 就不能保存模型; 而 'allowEmpty' => false 则像上面描述的寻,是确保这个列的 值 必须是非空的。
‘on’ 键可以被设置为 ‘update’ 或 ‘create’。它提供了允许一些规则应用于创建新记录的过程中或者更新记录的过程中的机制。
如果一条规则被定义成 ‘on’ => ‘create’,这条规则仅在创建新记录的过程中有效。类似的,如果它被定义成 ‘on’ => ‘update’,将只在更新记录的过程中有效。
‘on’ 的默认值为空(null)。当 ‘on’ 为空(null),这条规则在创建和更新过程中同时生效。
message 键为规则自定义校验错误时的显示信息:
public $validate = array(
'password' => array(
'rule' => array('minLength', 8),
'message' => 'Password must be at least 8 characters long'
)
);
上面的技术为我们提供了比简单的规则分配更大的灵活性,再进一步,我们能获得更详细的数据校验控制。下面我们介绍一种允许我们为每个列赋予多个规则的技术。
想要为单个列赋多个校验规则,基本的写法如下:
public $validate = array(
'fieldName' => array(
'ruleName' => array(
'rule' => 'ruleName',
// 类似 on,required 等扩展键放在这里...
),
'ruleName2' => array(
'rule' => 'ruleName2',
// 类似 on,required 等扩展键放在这里...
)
)
);
正像你看到的那样,这和上一节中所做的非常相似。在那儿,每个列仅有一个校验参数数组。在这儿,每个 ‘fieldName’ 是一个规则数组的索引。每个 ‘ruleName’ 包含一个校验参数数组。
下面是一个带有实际例子的更好诠释:
public $validate = array(
'login' => array(
'loginRule-1' => array(
'rule' => 'alphaNumeric',
'message' => 'Only alphabets and numbers allowed',
),
'loginRule-2' => array(
'rule' => array('minLength', 8),
'message' => 'Minimum length of 8 characters'
)
)
);
上例为 login 列定义了两个规则: loginRule-1 和 loginRule-2。如你所见,每个规则的标识随意命名。
在为单个列定义多条规则时,’required’ 和 ‘allowEmpty’ 键仅需在第一个规则中使用一次。
对于每个列有多条规则的情况,默认的如果一个规则校验失败并返回错误消息,该列的后续规则将不再计算。如果希望一个规则失败了校验仍然继续,就将这条规则的 last 键设置为 false。
在下面的例子中,即使 “rule1” 失败,”rule2” 仍将计算,并且在 “rule2” 也失败的时候返回所有的错误信息:
public $validate = array(
'login' => array(
'rule1' => array(
'rule' => 'alphaNumeric',
'message' => 'Only alphabets and numbers allowed',
'last' => false
),
'rule2' => array(
'rule' => array('minLength', 8),
'message' => 'Minimum length of 8 characters'
)
)
);
在用数组指定校验规则时,可以不使用 message 键。考虑下面的例子:
public $validate = array(
'login' => array(
'Only alphabets and numbers allowed' => array(
'rule' => 'alphaNumeric',
),
)
);
如果 alphaNumeric 规则失败,由于没有设置 message 键,这条规则的键 ‘Only alphabets and numbers allowed’ 将作为错误消息被返回。
如果找不到所需的校验规则,可以自定义。有两种方法进行自定义: 自定义正则表达式,或者创建自定义校验方法。
如果所需的校验手段能够通过正则表达式匹配来完成,可以自定义表达式作为列的校验规则:
public $validate = array(
'login' => array(
'rule' => '/^[a-z0-9]{3,}$/i',
'message' => 'Only letters and integers, min 3 characters'
)
);
上例检查了 login 是否仅包含字母和整数,并且不得小于3个字符。
“rule” 中的正则表达式必须用斜杠(/)括住。末尾的可选项 ‘i’ 表示正则表达式是不区分大小写的。
有时仅用正则模式校验数据还不够。 例如,想要确保促销码仅能被使用 25 次,需要添加自己的校验方法,示例如下:
class User extends AppModel {
public $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'This code has been used too many times.'
)
);
public function limitDuplicates($check, $limit) {
// $check 的值: array('promotion_code' => 'some-value')
// $limit 的值: 25
$existing_promo_count = $this->find('count', array(
'conditions' => $check,
'recursive' => -1
));
return $existing_promo_count < $limit;
}
}
被校验的当前列被传递给函数,作为该函数的第一个参数,是以列名为键,以 post 来的数据为值构成的关联数组。
如果想要给校验函数传递额外的参数,向 ‘rule’ 数组添加一个元素,并在函数中以扩展参数来处理它们(排在主参数$check 之后)。
校验函数可以放在模型中(就像上面的例子),也可以放在模型实现的行为中。包括映射方法。
模型/行为 方法最先校验,优先于在 Validation 类的方法。这意味着可以在应用程序级别(通过在 AppModel 添加方法)或模型级别覆盖已存在的校验方法(例如 alphaNumeric())。
在编写可用于多个列的校验规则,从 $check 数组中提取列值时要小心。$check 数组是以表单域名作为键、表单域值作为值构成的。存储在 $this->data 成员变量中的整个记录被校验。
class Post extends AppModel {
public $validate = array(
'slug' => array(
'rule' => 'alphaNumericDashUnderscore',
'message' => 'Slug can only be letters, numbers, dash and underscore'
)
);
public function alphaNumericDashUnderscore($check) {
// $data 数组是以表单域名为键被传递的
// 必须提取该值以使函数通用
$value = array_values($check);
$value = $value[0];
return preg_match('|^[0-9a-zA-Z_-]*$|', $value);
}
}
注解
自定义的校验方法的可见性必须是 public。不支持 protected 和 private 级别的校验方法。
如果规则有效,该方法返回 true。如果校验失败,返回 false。另一个有效的返回值是一个字符串,它是要显示的错误信息。返回字符串意味着校验失败。 这个字符串将覆盖 $validate 数组中的信息集,并将作为列为什么无效的原因显示在视图的表单中。
使用 $validate 属性定义校验规则是为每个模型定义静态规则的好办法。不过有时你需要从预定义的规则集中动态添加、改变或删除校验规则。
所有的校验规则都存储在 ModelValidator 对象中,它掌握着模型中各个列的每个规则集。 通过通知这个对象为想要的列存储新的校验方法来定义新的校验规则是很简单的。
2.2 新版功能.
ModelValidator 对象提供了数个向集中添加新列的途径。第一个是使用 add 方法:
// 在模型类中
$this->validator()->add('password', 'required', array(
'rule' => 'notEmpty',
'required' => 'create'
));
这样就会为模型中的 password 列添加单一规则。可以链式多次调用 add 来创建多条所需的规则:
// 在模型类中
$this->validator()
->add('password', 'required', array(
'rule' => 'notEmpty',
'required' => 'create'
))
->add('password', 'size', array(
'rule' => array('between', 8, 20),
'message' => 'Password should be at least 8 chars long'
));
也可以一次性为单个列添加多条规则:
$this->validator()->add('password', array(
'required' => array(
'rule' => 'notEmpty',
'required' => 'create'
),
'size' => array(
'rule' => array('between', 8, 20),
'message' => 'Password should be at least 8 chars long'
)
));
或者也可以利用数组接口使用此对象,直接为列设置规则:
$validator = $this->validator();
$validator['username'] = array(
'unique' => array(
'rule' => 'isUnique',
'required' => 'create'
),
'alphanumeric' => array(
'rule' => 'alphanumeric'
)
);
2.2 新版功能.
可以使用 validator 对象编辑现有的校验规则,有几种方法可以修改现有规则、向列追加方法或者从列规则集中完整地删除一条规则:
// 在模型类中
$this->validator()->getField('password')->setRule('required', array(
'rule' => 'required',
'required' => true
));
也可以使用类似的方法完整地替换一个列的所有规则:
// 在模型类中
$this->validator()->getField('password')->setRules(array(
'required' => array(...),
'otherRule' => array(...)
));
如果只是想领回规则中的单个属性,可以直接设置 CakeValidationRule 对象的属性:
// 在模型类中
$this->validator()->getField('password')
->getRule('required')->message = 'This field cannot be left blank';
CakeValidationRule 中的属性是由在模型中使用 $validate 属性定义相应规则的有效数组键来命名的。
在向规则集中添加规则时,也可以使用数组接口编辑已有的规则:
$validator = $this->validator();
$validator['username']['unique'] = array(
'rule' => 'isUnique',
'required' => 'create'
);
$validator['username']['unique']->last = true;
$validator['username']['unique']->message = 'Name already taken';
2.2 新版功能.
完整地删除一个列的所有规则和只删除一个列规则集中的单个规则都是可以的:
// 完成地删除一个列的所有规则
$this->validator()->remove('username');
// 从 password 中删除 'required' 规则
$this->validator()->remove('password', 'required');
还可以使用数组接口从规则集中删除规则:
$validator = $this->validator();
// 完整地删除一个列的所有规则
unset($validator['username']);
// 从 passworkd 中删除 'required' 规则
unset($validator['password']['required']);