yii2 使用场景验证(临时验证)请求参数的使用(model 校验规则)
场景的意义
模型可能在多个场景下使用,例如 User 模块可能会在收集用户登录输入, 也可能会在用户注册时使用。在不同的场景下, 模型可能会使用不同的业务规则和逻辑, 例如 email 属性在注册时强制要求有,但在登陆时不需要。(目前我的理解是场景主要用来做参数验证的)
使用示例(本示例以用户模型在注册,登录两个场景下的使用方法分析):
// 场景作为属性来设置 $model = new User; $model->scenario = 'login';
或
// 场景通过构造初始化配置来设置 $model = new User(['scenario' => 'login']);
默认情况下,模型支持的场景由模型中申明的 验证规则 来决定, 但你可以通过覆盖yii\base\Model::scenarios()方法来自定义行为, 如下所示:
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
public function scenarios()
{
return [
'login' => ['username', 'password'],
'register' => ['username', 'email', 'password'],
];
}
}
Info:在上述和下述的例子中,模型类都是继承 yii\db\ActiveRecord, 因为多场景的使用通常发生在 Active Record 类中。
scenarios() 方法返回一个数组,数组的键为场景名,值为对应的 active attributes 活动属性。 活动属性可被块赋值并遵循验证规则 在上述例子中,username 和 password 在login场景中启用,在 register 场景中, 除了 username and password 外 email 也被启用。
scenarios() 方法默认实现会返回所有 yii\base\Model::rules() 方法申明的验证规则中的场景, 当覆盖 scenarios() 时,如果你想在默认场景外使用新场景, 可以编写类似如下代码:
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
public function scenarios()
{
$scenarios = parent::scenarios();
'login' = ['username', 'password'];
'register' = ['username', 'email', 'password'];
return $scenarios;
}
}
场景特性主要在验证 和 属性块赋值 中使用。 你也可以用于其他目的, 例如可基于不同的场景定义不同的 属性标签。
验证规则
当模型接收到终端用户输入的数据, 数据应当满足某种规则(称为 验证规则, 也称为 业务规则)。 例如假定 ContactForm 模型,你可能想确保所有属性不为空且 email 属性包含一个有效的邮箱地址, 如果某个属性的值不满足对应的业务规则, 相应的错误信息应显示,以帮助用户修正错误。
可调用 yii\base\Model::validate() 来验证接收到的数据, 该方法使用 yii\base\Model::rules() 申明的验证规则来验证每个相关属性, 如果没有找到错误,会返回 true, 否则它会将错误保存在 yii\base\Model::errors 属性中并返回 false,例如:
$model = new \app\models\ContactForm;
// 用户输入数据赋值到模型属性
$model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) {
// 所有输入数据都有效 all inputs are valid
} else {
// 验证失败:$errors 是一个包含错误信息的数组
$errors = $model->errors;
}
通过覆盖 yii\base\Model::rules() 方法指定模型 属性应该满足的规则来申明模型相关验证规则。 下述例子显示 ContactForm 模型申明的验证规则:
public function rules()
{
return [
// name, email, subject 和 body 属性必须有值
[['name', 'email', 'subject', 'body'], 'required'],
// email 属性必须是一个有效的电子邮箱地址
['email', 'email'],
];
}
一条规则可用来验证一个或多个属性,一个属性可对应一条或多条规则。 更多关于如何申明验证规则的详情请参考 验证输入 一节.
有时你想一条规则只在某个 场景 下应用,为此你可以指定规则的 on 属性, 如下所示:
public function rules()
{
return [
// 在"register" 场景下 username, email 和 password 必须有值
[['username', 'email', 'password'], 'required', 'on' => 'register'],
// 在 "login" 场景下 username 和 password 必须有值
[['username', 'password'], 'required', 'on' => 'login'],
];
}
如果没有指定 on 属性,规则会在所有场景下应用, 在当前 yii\base\Model::scenario 下应用的规则称之为 active rule 活动规则。
一个属性只会属于 scenarios() 中定义的活动属性且在 rules() 申明对应一条或多条活动规则的情况下被验证。
下面是一个本人在具体的业务中写的一个 model 类,可供参考:
<?php
namespace common\models\qifaCn\invoice;
use common\helpers\Utils;
use Yii;
use yii\helpers\HtmlPurifier;
/**
* This is the model class for table "qifacn.qifacn_member_invoice_titles".
*
* 订单的电子发票的模型文件类
* @property int $invoice_title_id 发票抬头ID
* @property int $member_id 会员ID
* @property int $title_type 抬头类型;0 - 个人/非企业性单位;1 - 公司
* @property string $title 抬头名称
* @property string $taxpayer_id 纳税人识别号
* @property string $despoist_bank 开户银行(选填)
* @property string $bank_account 银行账号(选填)
* @property string $registered_address 注册地址(选填)
* @property string $registered_phone 注册电话(选填)
* @property string $created_at 创建时间
* @property string $updated_at 最后更新时间
*/
class QifacnMemberInvoiceTitles extends QifaCnActiveRecord {
public $order_no;
public $agree_type;
public $invoice_type;
const TITLE_TYPE_PERSONAL = 0;
const TITLE_TYPE_COMPANY = 1;
/**
* {@inheritdoc}
*/
public static function tableName(): string {
return 'qifacn.qifacn_member_invoice_titles';
}
/**
* {@inheritdoc}
*/
public function rules(): array {
return [
[['member_id', 'title_type'], 'integer'],
[['title_type', 'title'], 'required'],
[['taxpayer_id'], 'required', 'when' => function ($model) {
return $model->title_type == self::TITLE_TYPE_COMPANY;
}],
[['created_at', 'updated_at'], 'safe'],
[['job_no', 'username'], 'unique', 'message' => '{attribute} 为 {value} 已经存在!', 'targetClass' => ManufacturerCounty::class, 'filter' => function ($query) {
if (!$this->isNewRecord) {
$query->andWhere(['<>', 'manufacturer_county_id', $this->manufacturer_county_id]);
}
}],//唯一校验,编辑的时候排除当前的情况
[['title', 'despoist_bank', 'bank_account', 'registered_phone'], 'string', 'max' => 50],
[['taxpayer_id'], 'string', 'min' => 7, 'max' => 20, 'tooShort' => '{attribute}至少需要 {min} 个字', 'tooLong' => '{attribute}最多只能包含 {max} 个字'],
[['registered_phone'], 'string', 'max' => 20, 'tooLong' => '{attribute}最多只能包含 {max} 个字'],
['taxpayer_id', 'match', 'pattern' => '/^[a-zA-Z0-9]+$/', 'message' => '{attribute}仅允许输入数字或英文字符'],
['registered_phone', 'match', 'pattern' => '/^[0-9\-]+$/', 'message' => '{attribute}仅允许输入数字或中划线'],
[['order_no', 'invoice_type'], 'required', 'on' => ['apply']],
[['order_no'], 'each', 'rule' => ['integer'], 'on' => 'apply'],//on 的值可以是字符串可以是数组也可以是字符串,这里验证 order_no 的值是一个数组,并且数组里每个值都是 integer 类型
[['title_type', 'invoice_type'], 'in', 'range' => [self::TITLE_TYPE_PERSONAL, self::TITLE_TYPE_COMPANY]],
[['agree_type'], 'in', 'range' => [0, 1, 2], 'on' => 'electronic-invoice-list'],
[['registered_address'], 'string', 'max' => 200], //字符串类型,并且最大长度为 200 个字符
[['updated_at'], 'date', 'format' => 'php:Y-m-d H:i:s'],//确保 update_at 的值格式是 Y-m-d H:i:s 格式
[['title', 'despoist_bank', 'bank_account', 'registered_phone', 'registered_address', 'taxpayer_id'], 'filter', 'filter' => function ($value) {
return trim(htmlentities(strip_tags($value), ENT_QUOTES, 'UTF-8'));//过滤这些字段的值
}],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels(): array {
return [
'invoice_title_id' => '订单电子发票抬头ID',
'member_id' => Yii::t('app', 'member_id'),
'title_type' => '抬头类型',
'title' => '抬头名称',
'taxpayer_id' => '发票税号',
'despoist_bank' => '开户银行',
'bank_account' => '银行账号',
'registered_address' => '注册地址',
'registered_phone' => '注册电话',
'created_at' => Yii::t('app', 'created_at'),
'updated_at' => Yii::t('app', 'updated_at'),
];
}
/**
* @return \string[][]
*/
public function scenarios(): array {
return array_merge(parent::scenarios(), [
'apply' => ['order_no', 'invoice_type', 'title_type', 'title', 'taxpayer_id', 'despoist_bank', 'bank_account', 'registered_address', 'registered_phone', 'member_id'],//申请电子发票场景
'header-save' => ['title_type', 'title', 'taxpayer_id', 'despoist_bank', 'bank_account', 'registered_address', 'registered_phone', 'member_id'],//保存电子发票抬头场景
'default' => ['title_type', 'title', 'taxpayer_id', 'despoist_bank', 'bank_account', 'registered_address', 'registered_phone'],
'electronic-invoice-list' => ['agree_type']//获取电子发票列表场景
]);
}
/**
* @param int $member_id
* @param int $titleType
* @return array|\yii\db\ActiveRecord[]
*/
public static function getListByMemberId($member_id, $titleType = -1) {
$query = self::find()->where(['member_id' => $member_id]);
$titleType >= 0 && $query->andWhere(['title_type' => $titleType]);
return $query->asArray()->all();
}
}
临时验证
有时,你需要对某些没有绑定任何模型类的值进行临时验证。那么可参考:
$model = DynamicModel::validateData($params, [
[['type'], 'required'],
[['date'], 'required', 'when' => function ($model) {
return in_array($model->type, ['DayStatisticsList', 'MonthStatisticsList']);
}],
[['uid'], 'required', 'when' => function ($model) {
return in_array($model->type, ['UserMonthStatisticsDetail']);
}],
[['type', 'date'], 'string'],
[['uid'], 'integer'],
[['type'], 'in', 'range' => ['DayStatisticsList', 'MonthStatisticsList', 'MonthStatisticsDetail', 'UserMonthStatisticsDetail']],
]);
if ($model->hasErrors()) {
throw new MerakException(array_values($model->getFirstErrors())[0]);
}
微信扫一扫,打赏作者吧~