首页 > 解决方案 > Yii2 - 验证嵌套对象

问题描述

这是一个关于我已经思考了一段时间的话题的问题。

在 Yii2 中,一般建议为您的请求创建表单模型。将规则添加到这些模型以验证输入。Yii2 指南中的 EntryForm 就是一个例子

<?php

namespace app\models;

use Yii;
use yii\base\Model;

class EntryForm extends Model
{
    public $name;
    public $email;

    public function rules()
    {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}

我的问题是,当我们有嵌套对象时。一个示例是用于创建具有多个Branches的Customer的表单。如果CustomerBranch是两个独立的模型,但都以一个表单提交,那么验证来自这种嵌套表单的输入的最佳选择是什么。请记住,这里的输入是嵌套的。例子:

{
  "name": "customer",
  "vat_no": "12345678",
  "time_zone": 277,
  "category": 1,
  "email": "customer@mycustomer.com",
  "stores":[
    {
        "name": "store1",
        "phone": 1234567
    },
    {
        "name": "store2",
        "phone": 2345678
    }
]
}

标签: yiiyii2

解决方案


对于简单的情况,您可以在表单模型中使用一个模型和自定义验证器:

public function rules() {
    return [
        // ...
        ['stores', 'validateStores'],
    ];
}

public function validateStores() {
    $phoneValidator = new StringValidator(); // use real validators
    $nameValidator = new StringValidator(); // use real validators
    foreach ($this->stores as $store) {
        if (!$phoneValidator->validate($store['phone'], $error)) {
            $this->addError('stores', $error);
            return; // stop on first error
        }
        if (!$nameValidator->validate($store['name'], $error)) {
            $this->addError('stores', $error);
            return; // end on first error
        }
    }
}

validateStores()可以提取到单独的验证器类,那么你也可以使用EachValidator代替foreach.


对于更复杂的嵌套模型,您可能应该StoreForm为商店创建单独的模型(因此您将拥有嵌套表单模型),并调用validate()子代。

/**
 * @var StoreForm[]
 */
public $stores;

public function rules() {
    return [
        // ...
        ['stores', 'validateStores'],
    ];
}

public function validateStores() {
    foreach ($this->stores as $store) {
        if (!$store->validate()) {
            $this->addError('stores', 'Stores config is incorrect.');
            return;
        }
    }
}

推荐阅读