Yii学习笔记之四(表单验证 api 翻译)

时间:2023-03-09 04:10:22
Yii学习笔记之四(表单验证 api 翻译)

1.表单验证

对于用户输入的全部数据,你不能信任,必须加以验证。

全部框架如此。对于yii 能够使用函数

 yii\base\Model::validate()  进行验证

他会返回boolean值的 true /false

假设验证未通过,能够使用

yii\base\Model::$errors 属性进行处理,例如以下代码:

<?php

 //载入表单模型(绝对地址方式)
/*假设上面引入
use app\models\ContactForm;
则能够直接使用
$model = new ContactForm;
*/
$model = new \app\models\ContactForm; // 接收用户填充的模型的字段 翻入属性
$model->attributes = \Yii::$app->request->post('ContactForm'); if ($model->validate()) {
// 全部的输入验证通过
} else {
// 验证失败: $errors 是一个包括全部错误信息的数组
$errors = $model->errors;
} ? >

2. 声明规则

为了使验证函数 validate() 真正起作用,我们须要定一些验证

规则相应这些表单进行相应的验证,他会自己主动重写

yii\base\Model::rules() 方法

以下的样例讲述怎样使用 rules

<?

php

public function rules()
{
return [
// name, email, subject 和 body 属性必须填写
[['name', 'email', 'subject', 'body'], 'required'], //email 必须是一个有效的邮箱地址
['email', 'email'],
];
} //rule 返回值的格式 [
// 必须的參数一, 指定必须遵守这个规则的某个属性或某些属性.
// 对一个单个的属性,你能直接使用属性名
// 不必将他放进一个数组
['attribute1', 'attribute2', ...], //对于多个则能够放进一个数组 // 必须的參数二, 指定规则的类型.
// 他能够是一个类名, 验证方法的别名, 或者是验证方法名
'validator', // 可选的參数三, 指定那一个场景使用这个规则
// 假设没给, 意味着这个规则全部的场景都要将适用
// 你可能也要配置 "except" 规则假设你想使用这个规则
// to all scenarios except the listed ones
'on' => ['scenario1', 'scenario2', ...], // 可选的參数, 定制额外的配置对验证项目
'property1' => 'value1', 'property2' => 'value2', ...
] ? >

1> 对于每个规则。你必须至少指定哪些属性的规则适用于什么是规则的类型。

您能够在下列形式之中的一个指定的规则类型:



(1) 核心验证的别名, 比方 required, in, date, 等等. 请參阅核心验证器为核心的校验器的完整列表。

http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html



(2) 在模型类model class中的验证方法的名字, 或一个匿名函数. 请參考内联验证器的具体信息.

http://www.yiiframework.com/doc-2.0/guide-input-validation.html#inline-validators



(3) 一个全然合格的验证器类名. 请參考标准验证器

http://www.yiiframework.com/doc-2.0/guide-input-validation.html#standalone-validators



一个规则能够用于一个或多个属性验证, 一个属性能够被一个或多个规则验证. 规则能够在某些情况下

仅仅能通过选项指定的应用。假设您不选择指定,这意味着该规则将适用于全部情况。



2> 当 validate() 方法被调用的时候, 以下的步骤来解释验证的过程:



(1) 首先确定哪些属性应该通过获得从属性列表中进行验证

  yii\base\Model::scenarios() 使用当前的场景.这些属性被称为活跃属性。



(2) 确定哪些验证规则将会被使用通过获取 rule 列表从

 yii\base\Model::rules() 在当前的场景中使用. 这些规则被称为活跃规则。



(3) 用一个活跃的规则去验证每个活跃的与相应规则相关联的属性.

   这些验证规则将依据他们列出的顺序顺序运行。

3. 定制错误信息

大多数验证有默认的错误信息,在验证失败的时候显示出来.比如 required 验证能够加入一句 "Username cannot be blank." 将会在username 没有填写的时候自己主动出现,例如以下:

<?

php
public function rules()
{
return [
['username', 'required', 'message' => 'Please choose a username.'],
];
} ?>

有些可能验证支持附加的额外的错误消息,以更准确地描写叙述验证失败不同的原因。比如,数字验证器支持tooBig和tooSmall来描写叙述验证失败时要验证的值分别为过大。过小。您能够配置这些错误信息就像一个验证规则配置验证的其它属性一样。

4. 验证事件

当 yii\base\Model::validate() 被调用的时候, 它将会自己主动调用两个自己定义的方法:

http://www.yiiframework.com/doc-2.0/yii-base-model.html#beforeValidate()-detail



(1) yii\base\Model::beforeValidate(): 默认的实现会自己主动触发一个 yii\base\Model::EVENT_BEFORE_VALIDATE 事件. 你也能够重写这种方法或响应这个事件做一些预处理工作 (e.g. 正常化的数据输入) 在验证事件发生之前. 这种方法应该返回一个布尔值,指示验证是否应该继续与否。



(2) yii\base\Model::afterValidate(): 默认的实现会自己主动触发一个yii\base\Model::EVENT_AFTER_VALIDATE  事件.你也能够重写这种方法或响应这个事件在验证完毕后做一些处理工作。

5. 条件验证

为了验证仅仅有当某些条件适用属性, e.g. 一个属性的有效性依赖于还有一个属性的值。你能够使用当属性来定义这种条件。比如,'state' 仅仅有当 'model'的属性值 'country' 为 'USA' 才成立

<?php
[
['state', 'required', 'when' => function($model) {
return $model->country == 'USA';
}],
] ?>

这个 when 属性携带了一个PHP调用带着以下的签名:

<?

php
/**
* @param Model $model 正在被验证的model
* @param string $attribute 正在被验证的属性
* @return boolean 是否这个验证成功了
*/
function ($model, $attribute)

假设你也想支持client条件验证,你应该配置这个 whenClient 属性,以一个字符串形式传递

JavaScript 函数代码返回这个规则是否正确. 比如,

[
['state', 'required', 'when' => function ($model) {
return $model->country == 'USA';
}, 'whenClient' => "function (attribute, value) {
return $('#country').val() == 'USA';
}"],
]

6. 数据过滤

用户输入的数据常常要预处理或过滤. 比如, 你可能想去掉username input两端的空格.

你能够使用验证规则去达到这个效果。



以下的样例就是去掉空格inputs 将孔的输入框转换为 nulls通过使用 trim 和 default两个核心验证器:

<?php
[
[['username', 'email'], 'trim'],
[['username', 'email'], 'default'],
]
? >

你能够展示更一般的过滤去完毕更复杂的数据过滤.



就像你看到的,这些验证并不验证输入.相反,他们将输入通过程序返回给验证器.

7. 处理空的输入

当 HTML forms 表单数据被提交的时候, 你通常须要分配一些默认的值给这些 inputs 假设他们是空的话. 你也能够这么做使用默认的验证 例如以下代码:

<?

php
[
// 设置 "username" 和 "email" 值为 null 假设没填的话
[['username', 'email'], 'default'], // 设置 "level" 为 1 假设为空的话
['level', 'default', 'value' => 1],
]
?>

对于默认验证, 一个 input会被考虑是空的 假设他的值是一个空的字符串, 一个空的数组array 或者是一个 null. 你能够自己定义这个默认值的逻辑通过配置

yii\validators\Validator::isEmpty() 属性作为一个 PHP调用. 比如:

<?

php
[
['agree', 'required', 'isEmpty' => function ($value) {
return empty($value);
}],
]
?>

提示: 大多数验证不会处理空的 inputs 假设他们的

   yii\base\Validator::skipOnEmpty 属性带着默认的 true 值. 不过captcha, default, filter, required,和
trim 验证器会处理空的 inputs.

8. 特设验证

有时候你须要做一个特别验证。对某些没有绑定到不论什么 model的值



假设你仅仅须要运行一个类型的验证 (e.g. 验证 email 地址), 你能够调用 validate()
方法去找到对应的验证,

就像想以下这样:

<?php
$email = 'test@example.com';
$validator = new yii\validators\EmailValidator(); if ($validator->validate($email, $error)) {
echo 'Email is valid.';
} else {
echo $error;
} ?>

提示: 并非全部的验证器都支持这样的类型验证. 一个样例是独特的核心验证,其目的是仅仅用模式来工作。

假设须要对多个值运行多个验证,您能够使用

 yii\base\DynamicModel 能够同一时候声明属性 和 规则. 使用方式例如以下:

<?

php

public function actionSearch($name, $email)
{
$model = DynamicModel::validateData(compact('name', 'email'), [
[['name', 'email'], 'string', 'max' => 128],
['email', 'email'],
]); if ($model->hasErrors()) {
// 验证失败代码
} else {
// 验证成功代码
}
} ?>

这个

 yii\base\DynamicModel::validateData()

方法创建了一个 DynamicModel 的实例,定义了使用给定的数据的属性(name 和 email 在这个样例中),

并且之后调用 yii\base\Model::validate() 携带给定的规则.



或者你能够使用  "classic" 语法进行上面的验证:

<?php
public function actionSearch($name, $email)
{
$model = new DynamicModel(compact('name', 'email'));
$model->addRule(['name', 'email'], 'string', ['max' => 128])
->addRule('email', 'email')
->validate(); if ($model->hasErrors()) {
// 验证失败代码
} else {
// 验证成功代码
}
} ?>

验证之后, 你能够检查验证是否成功不通过调用 hasErrors() 方法, 之后得到验证错误来自 errors属性,

就像你在处理一个正常的 model. 你也能够直接訪问传统的属性通过 model 实例定义的,

比如, $model->name 和 $model->email.

10. 创建验证器

除了使用包括在Yii公布的 core validators 外, 你也能够创建自己的验证器. 你能够创建一个 内联 validators 独立 validators.



(1) Inline Validators(内置验证器)



一个 inline validator 是一个 model method 或一个匿名 function. 改 method/function 的原型是:

<?php
/**
* @param string $attribute 当前被验证的属性
* @param mixed $params 规则里给定的參数
*/
function ($attribute, $params)
? >

假设一个属性验证失败, 这个 method/function 将会调用

yii\base\Model::addError()

去保存错误信息在 model 以便于他能在遍历的时候反馈呈现给客户.



以下是给定的样例:

<?

php
use yii\base\Model; class MyForm extends Model
{
public $country;
public $token; public function rules()
{
return [
// 一个 inline validator 定义作为一个 model 方法 validateCountry()
['country', 'validateCountry'], // 一个 inline validator 定义作为一个匿名 function
['token', function ($attribute, $params) {
if (!ctype_alnum($this->$attribute)) { ////推断是否是字母和数字或字母数字的组合
$this->addError($attribute, 'The token must contain letters or digits.');
}
}],
];
} public function validateCountry($attribute, $params)
{
if (!in_array($this->$attribute, ['USA', 'Web'])) {
$this->addError($attribute, 'The country must be either "USA" or "Web".');
}
}
}
? >

提示: 假设他们的相关属性接收空的输入或者他们已经验证失败一些规则通过 default, inline validators 将不会被用。

假设你想确保一个规则始终适用。您能够配置skipOnEmpty 和/或 skipOnError性质是 false 的规则声明。

比如:

<?

php
[
['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],
]
? >

(2) Standalone Validators (单独验证器)

一个 standalone validator 是一个类扩展

yii\validators\Validator 或者是他的子类



你能够实现他的验证逻辑通过重写

yii\validators\Validator::validateAttribute() 方法.




假设一个属性验证失败, 调用

yii\base\Model::addError()

去保存错误信息在 model 里, 就像上面 的 inline validators 所做的. 比如,

<?

php

namespace app\components;

use yii\validators\Validator;

class CountryValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
if (!in_array($model->$attribute, ['USA', 'Web'])) {
$this->addError($model, $attribute, 'The country must be either "USA" or "Web".');
}
}
} ?>

假设你想你的验证器支持验证一个model外的值,你也应该重写


yii\validators\Validator::validate().

你可能也要重写

yii\validators\Validator::validateValue() 取代 validateAttribute() 和 validate()


由于默认的后两种方法通过调用 validateValue() 实现.

11. client验证

client验证基于 JavaScript .



Info:  yii\base\Model::validate(),

使用  yii\widgets\ActiveForm 创建你的 HTML forms.

比如, LoginForm 须要声明两条规则:

      A. 必填 required; 既支持client 又支持服务端

      B. password验证 validatePassword 使用 inline validator 仅仅支持服务端

<?php
namespace app\models; use yii\base\Model;
use app\models\User; class LoginForm extends Model
{
public $username;
public $password; public function rules()
{
return [
// username 和 password 都不能为空
[['username', 'password'], 'required'], // password 通过 validatePassword() 验证
['password', 'validatePassword'],
];
} public function validatePassword()
{
$user = User::findByUsername($this->username); if (!$user || !$user->validatePassword($this->password)) {
$this->addError('password', 'Incorrect username or password.');
}
}
}
?>

以下的代码创建俩表单 username 和 password.


假设你提交 form 的时候没有输入不论什么东西, 你会发现 requiring 的错误信息.

<?php $form = yii\widgets\ActiveForm::begin(); ?>
<?= $form->field($model, 'username') ? >
<? = $form->field($model, 'password')->passwordInput() ?>
<? = Html::submitButton('Login') ?>
<?php yii\widgets\ActiveForm::end(); ?>

以下的情况,

yii\widgets\ActiveForm

会读取定义在 model 中的验证规则 然后生成相近的 JavaScript 代码用来支持对应的client代码.当一个用户改变输入框input 得值 或者 提交submit 表单form, client的javascript会自己主动触发.



假设你想全然关闭client验证, 你能够配置:

yii\widgets\ActiveForm::$enableClientValidation 的属性为 false.

你也能够关闭部分的输入表单验证通过配置他们的

yii\widgets\ActiveField::$enableClientValidation 的属性为 false.



实现client验证

为了创建一个支持client的验证, 你须要实现

yii\validators\Validator::clientValidateAttribute() 方法

返回在client运行的部分 JavaScript 代码.

在 JavaScript 代码里, 你能够使用以下提前定义的变量:



attribute: 即将被验证的属性.

value: 被验证的值.

messages: 一个 array 用于处理 attribute 验证的错误信息.

deferred: 一个 array 放入延期的对象.

在以下的样例中。我们创建一个StatusValidator这验证假设输入是针对现有的状态数据的有效状态输入。

验证器同一时候支持server端和client验证

<?php
namespace app\components; use yii\validators\Validator;
use app\models\Status; class StatusValidator extends Validator
{
public function init()
{
parent::init();
$this->message = 'Invalid status input.';
} public function validateAttribute($model, $attribute)
{
$value = $model->$attribute;
if (!Status::find()->where(['id' => $value])->exists()) {
$model->addError($attribute, $this->message);
}
} public function clientValidateAttribute($model, $attribute, $view)
{
$statuses = json_encode(Status::find()->select('id')->asArray()->column());
$message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
//返回js代码
return <<<JS
if (!$.inArray(value, $statuses)) {
messages.push($message);
}
JS;
}
} ?>

小贴士: The above code is given mainly to demonstrate how to support client-side validation. 在实践中, 你能够用核心验证器达到同样的效果. 你也能够像以下一样填写验证规则:

<?php
[
['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],
] ?>

12. 延迟验证

假设你须要运行一个client异步验证, 你能够创建一个延迟对象集. 比如, 去运行一个 AJAX 验证, 你能够使用以下的代码:

<?

php
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
deferred.push($.get("/check", {value: value}).done(function(data) {
if ('' !== data) {
messages.push(data);
}
}));
JS;
} ?>

在上面的样例中, 变量 deferred是 Yii 提供的, 是一个对象数组.  $.get() 是 jQuery 方法,

创建一个 Deferred object 并被放入 deferred 数组里.



你也能够精确的创建一个 Deferred object 接着调用他的 resolve() 当异步回调函数被触发的时候.

以下的演示样例演示怎样验证在client上传的图像文件的尺寸。

<?php
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
var def = $.Deferred();
var img = new Image();
img.onload = function() {
if (this.width > 150) {
messages.push('Image too wide!!');
}
def.resolve();
}
var reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file); deferred.push(def);
JS;
} ?>

提示: resolve() 必须被调用在属性被验证之后. 由于主表单验证还没有运行.

为了简单起见, 这个 deferred 配置了一个简单方法 add() 会自己主动创建一个 Deferred object 然后 将他加入到

deferred 数组. 使用这种方法, 你能将上面的代码简化成以下的:

<?php
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
deferred.add(function(def) {
var img = new Image();
img.onload = function() {
if (this.width > 150) {
messages.push('Image too wide!!');
}
def.resolve();
}
var reader = new FileReader();
reader.onloadend = function() {
img.src = reader.result;
}
reader.readAsDataURL(file);
});
JS;
} ? >

13. AJAX 验证

去启用 AJAX 为整个form, 你不得不设置

yii\widgets\ActiveForm::$enableAjaxValidation 属性值为 true


并且要指定id 成为一个唯一的 form 定义:

<?

php $form = yii\widgets\ActiveForm::begin([
'id' => 'contact-form',
'enableAjaxValidation' => true,
]); ? >

对于部分字段的ajax 验证是否启用,你能够设置相应属性的

yii\widgets\ActiveField::$enableAjaxValidation 属性.



您还须要准备server端代码,以便它能够处理Ajax验证请求。

这能够通过一个代码段像在控制器动作下面来实现:

<?php
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
} ?>

上面的代码检查当前的请求是否是一个 AJAX. 假设是, 他将响应这个请求执行验证然后返回错误信息以JSON格式.



信息: 你也能够使用 Deferred 去运行 AJAX 验证. 可是,这里所描写叙述的AJAX验证的特点是更系统,

更须要较少的编码工作。