0

Here's a question about a topic that Ive been thinking about for a while.

In Yii2, it is recommended generally to create Form Models for your requests. Rules are added to those models to validate the input. An example is the EntryForm in the Yii2 guide

<?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'],
        ];
    }
}

My problem is, when we have nested objects. An example is a form for creating Customer with multiple Branches. If Customer and Branch are two separate models, but both get submitted in one form, what is the best option for validating input from such a nested form. Bear in mind that here the input is nested. Example:

{
  "name": "customer",
  "vat_no": "12345678",
  "time_zone": 277,
  "category": 1,
  "email": "[email protected]",
  "stores":[
    {
        "name": "store1",
        "phone": 1234567
    },
    {
        "name": "store2",
        "phone": 2345678
    }
]
}
5
  • you can still use the formmodel to add all your rules in that model rather than the Base model and both the models are validated via FormModel simple as that. Commented Jun 6, 2018 at 13:49
  • means you can list down all the validation rules in FormModel and your Customer and Address model can be clean with basic rules which define the fields as safe , strings , integer or required Commented Jun 6, 2018 at 13:51
  • I'll edit the question to mention some other scenario that I should have mentioned :D Commented Jun 6, 2018 at 14:17
  • This is the example that I meant @MuhammadOmerAslam, but I wasnot clear when I first asked :) Commented Jun 6, 2018 at 14:26
  • rob006 has posted the answer below it provides the correct approach. Commented Jun 6, 2018 at 15:22

1 Answer 1

2

For simple cases you may use one model and custom validator inside of your form model:

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() may be extracted to separate validator class, then you may also use EachValidator instead of foreach.


For more complicated nested models you should probably create separate StoreForm model for stores (so you will have nested form models), and call validate() on children.

/**
 * @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;
        }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I like this approach. But one more question here :) How much do you think that I should rely on the original model validation? In this example those would be Customer and Branch
It is hard to say, it really depends on application, so there is no one right answer here. In one app I don't even have a validation in AR and everything is validated in form models. Sometimes AR is enough and you don't need form model.
Ok thanks a lot. I really like this approach. I'll try it :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.