<p>In your <code>Micros_GuestRegistrationRequestModel</code>'s attribute definition, if you define <code>person</code> like so:</p>

<pre><code>'person' =&gt; array(AttributeType::Mixed, 'model' =&gt; 'Micros_GuestPersonModel');
</code></pre>

<p>Craft should take care of ensuring <code>person</code> will be an instance of <code>Micros_GuestPersonModel</code>.</p>

CC BY-SA 3.0
2015-04-07T20:03:07.630Z
Great, thanks. Are there any validation helpers or is my validatePerson() method above the way to go?
Russ Back
CC BY-SA 3.0
2015-04-07T20:06:08.933Z
Honestly, I can't recall if calling validate on the main model will run validation for attributes that have a model class. Would be easy to test, though.
Brad Bell
CC BY-SA 3.0
2015-04-07T20:24:25.933Z
Hmmm, doesn't seem to work for me - I can pass anything into the attribute and all it seems to do is ensure something has been passed (required). It would be great if a model's validate method could also validate attribute models too at some point. I'll add that in the form of a custom validator for now.
Russ Back
CC BY-SA 3.0
2015-04-07T20:32:44.197Z
Yeah, custom validation is the way to go for now, then. You can always add a [feature request](http://feedback.buildwithcraft.com/)!
Brad Bell
CC BY-SA 3.0
2015-04-07T20:36:57.167Z
Done! Thanks Brad. Setting a model is pretty neat - I can see that if I pass an array it populates the model for me automatically, which saves some boilerplate :-)
Russ Back
CC BY-SA 3.0
2015-04-07T18:57:10.087Z
2015-04-07T18:57:10.087Z
Brad Bell
<p>I have an API call to make that needs a particular format of data shown below. The <strong>person</strong> and <strong>addresses</strong> attributes are currently set as <strong>Mixed</strong> <em>AttributeTypes</em> and I'll need to create a manual validation method on the model to make sure they are correctly formatted and populated:</p>

<pre><code>$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  =&gt; 'russ01@someemail.com',
    'password' =&gt; 'secret',
    'person' =&gt; [
        'firstName' =&gt; 'Sammy',
        'lastName' =&gt; 'Davis Junior'
    ], 'addresses' =&gt; [
        [
            'line1' =&gt; 'Any House',
            'line2' =&gt; 'Any Road',
            'town' =&gt; 'Anyville',
            'postcode' =&gt; 'AB1 1AB',
            'country' =&gt; 'GB',
            'stateCode' =&gt; 'AL'
        ]
    ]
]);
</code></pre>

<p>What I'm wondering is if <strong>person</strong> and <strong>addresses</strong> should be models in their own right, something like this for <strong>person</strong>:</p>

<pre><code>$person = Micros_GuestPersonModel::populateModel([
    'firstName' =&gt; 'Sammy',
    'lastName' =&gt; 'Davis Junior'
]);

$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  =&gt; 'russ01@fuel-digital.com',
    'password' =&gt; 'secret',
    'person' =&gt; $person,
    'addresses' =&gt; [
        [
            'line1' =&gt; 'Any House',
            'line2' =&gt; 'Any Road',
            'town' =&gt; 'Anyville',
            'postcode' =&gt; 'AB1 1AB',
            'country' =&gt; 'GB',
            'stateCode' =&gt; 'AL'
        ]
    ]
]);
</code></pre>

<p>I can't see a way to set a model attribute to be a particular class like <em>Micros_GuestPersonModel</em>, so I guess I could pass in the array of data from it like this:</p>

<pre><code>$person = Micros_GuestPersonModel::populateModel([
    'firstName' =&gt; 'Sammy',
    'lastName' =&gt; 'Davis Junior'
]);

$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  =&gt; 'russ01@fuel-digital.com',
    'password' =&gt; 'secret',
    'person' =&gt; $person-&gt;getAttributes(),
    'addresses' =&gt; [
        [
            'line1' =&gt; 'Any House',
            'line2' =&gt; 'Any Road',
            'town' =&gt; 'Anyville',
            'postcode' =&gt; 'AB1 1AB',
            'country' =&gt; 'GB',
            'stateCode' =&gt; 'AL'
        ]
    ]
]);
</code></pre>

<p>Can anybody offer advice here on how best to approach this kind of model setup?</p>

<p>==== EDIT =====</p>

<p>Here's my attempt at passing in a <strong>Micros_GuestPersonModel</strong> and validating it...</p>

<p><strong>Class <em>Micros_GuestPersonModel</em>:</strong></p>

<pre><code>class Micros_GuestPersonModel extends BaseModel {

    protected function defineAttributes ()
    {
        return [
            'firstName' =&gt; [
                'type' =&gt; AttributeType::String, 'required' =&gt; TRUE
            ],
            'lastName' =&gt; [
                'type' =&gt; AttributeType::String, 'required' =&gt; TRUE
            ]
        ];
    }

}
</code></pre>

<p><strong>Class <em>Micros_GuestRegistrationRequestModel</em>:</strong></p>

<pre><code>class Micros_GuestRegistrationRequestModel extends BaseModel {

    protected function defineAttributes ()
    {
        return [
            'username' =&gt; [
                'type' =&gt; AttributeType::Email, 'required' =&gt; TRUE
            ],
            'password' =&gt; [
                'type' =&gt; AttributeType::String, 'required' =&gt; TRUE
            ],
            'person' =&gt; [
                'type' =&gt; AttributeType::Mixed, 'required' =&gt; TRUE
            ],
            'address' =&gt; [
                'type' =&gt; AttributeType::Mixed, 'required' =&gt; TRUE
            ]
        ];
    }

    public function rules ()
    {
        $rules = parent::rules();

        $rules[] = [ 'person', 'validatePerson' ];

        return $rules;
    }

    public function validatePerson ($attribute)
    {
        if ( ! $this-&gt;$attribute instanceof Micros_GuestPersonModel)
        {
            $this-&gt;addError($attribute, "Person should be an instance of Micros_GuestPersonModel");

            return;
        }

        if ( ! $this-&gt;$attribute-&gt;validate())
        {
            foreach ($this-&gt;$attribute-&gt;getErrors() as $errors)
            {
                foreach ($errors as $error)
                {
                    $this-&gt;addError($attribute, $error);
                }
            }
        }
    }

}
</code></pre>

<p><strong>And an example controller usage:</strong></p>

<pre><code>    $person = Micros_GuestPersonModel::populateModel([
        'firstName' =&gt; '', // invalid as this is required
        'lastName' =&gt; 'Davis Junior'
    ]);

    $requestModel = Micros_GuestRegistrationRequestModel::populateModel([
        'username'  =&gt; 'russ01@fuel-digital.com',
        'password' =&gt; 'secret',
        'person' =&gt; $person,
        'addresses' =&gt; [
            [
                'line1' =&gt; 'Any House',
                'line2' =&gt; 'Any Road',
                'town' =&gt; 'Anyville',
                'postcode' =&gt; 'AB1 1AB',
                'country' =&gt; 'GB',
                'stateCode' =&gt; 'AL'
            ]
        ]
    ]);
</code></pre>

<p>I can then capture the errors as follows:</p>

<pre><code>if ( ! $requestModel-&gt;validate())
{
    Craft::dd($requestModel-&gt;getErrors());
}
</code></pre>

<p>It works, but is there a better way?</p>

<plugin-development><models><attribute>
Is there a good way of setting a model attribute to be an instance of another model?
# Is there a good way of setting a model attribute to be an instance of another model?

(Asked by: username_0 on 2015-04-07)

I have an API call to make that needs a particular format of data shown below. The person and addresses attributes are currently set as Mixed AttributeTypes and I'll need to create a manual validation method on the model to make sure they are correctly formatted and populated:
$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  => 'russ01@someemail.com',
    'password' => 'secret',
    'person' => [
        'firstName' => 'Sammy',
        'lastName' => 'Davis Junior'
    ], 'addresses' => [
        [
            'line1' => 'Any House',
            'line2' => 'Any Road',
            'town' => 'Anyville',
            'postcode' => 'AB1 1AB',
            'country' => 'GB',
            'stateCode' => 'AL'
        ]
    ]
]);

What I'm wondering is if person and addresses should be models in their own right, something like this for person:
$person = Micros_GuestPersonModel::populateModel([
    'firstName' => 'Sammy',
    'lastName' => 'Davis Junior'
]);

$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  => 'russ01@fuel-digital.com',
    'password' => 'secret',
    'person' => $person,
    'addresses' => [
        [
            'line1' => 'Any House',
            'line2' => 'Any Road',
            'town' => 'Anyville',
            'postcode' => 'AB1 1AB',
            'country' => 'GB',
            'stateCode' => 'AL'
        ]
    ]
]);

I can't see a way to set a model attribute to be a particular class like Micros_GuestPersonModel, so I guess I could pass in the array of data from it like this:
$person = Micros_GuestPersonModel::populateModel([
    'firstName' => 'Sammy',
    'lastName' => 'Davis Junior'
]);

$requestModel = Micros_GuestRegistrationRequestModel::populateModel([
    'username'  => 'russ01@fuel-digital.com',
    'password' => 'secret',
    'person' => $person->getAttributes(),
    'addresses' => [
        [
            'line1' => 'Any House',
            'line2' => 'Any Road',
            'town' => 'Anyville',
            'postcode' => 'AB1 1AB',
            'country' => 'GB',
            'stateCode' => 'AL'
        ]
    ]
]);

Can anybody offer advice here on how best to approach this kind of model setup?
==== EDIT =====
Here's my attempt at passing in a Micros_GuestPersonModel and validating it...
Class Micros_GuestPersonModel:
class Micros_GuestPersonModel extends BaseModel {

    protected function defineAttributes ()
    {
        return [
            'firstName' => [
                'type' => AttributeType::String, 'required' => TRUE
            ],
            'lastName' => [
                'type' => AttributeType::String, 'required' => TRUE
            ]
        ];
    }

}

Class Micros_GuestRegistrationRequestModel:
class Micros_GuestRegistrationRequestModel extends BaseModel {

    protected function defineAttributes ()
    {
        return [
            'username' => [
                'type' => AttributeType::Email, 'required' => TRUE
            ],
            'password' => [
                'type' => AttributeType::String, 'required' => TRUE
            ],
            'person' => [
                'type' => AttributeType::Mixed, 'required' => TRUE
            ],
            'address' => [
                'type' => AttributeType::Mixed, 'required' => TRUE
            ]
        ];
    }

    public function rules ()
    {
        $rules = parent::rules();

        $rules[] = [ 'person', 'validatePerson' ];

        return $rules;
    }

    public function validatePerson ($attribute)
    {
        if ( ! $this->$attribute instanceof Micros_GuestPersonModel)
        {
            $this->addError($attribute, "Person should be an instance of Micros_GuestPersonModel");

            return;
        }

        if ( ! $this->$attribute->validate())
        {
            foreach ($this->$attribute->getErrors() as $errors)
            {
                foreach ($errors as $error)
                {
                    $this->addError($attribute, $error);
                }
            }
        }
    }

}

And an example controller usage:
    $person = Micros_GuestPersonModel::populateModel([
        'firstName' => '', // invalid as this is required
        'lastName' => 'Davis Junior'
    ]);

    $requestModel = Micros_GuestRegistrationRequestModel::populateModel([
        'username'  => 'russ01@fuel-digital.com',
        'password' => 'secret',
        'person' => $person,
        'addresses' => [
            [
                'line1' => 'Any House',
                'line2' => 'Any Road',
                'town' => 'Anyville',
                'postcode' => 'AB1 1AB',
                'country' => 'GB',
                'stateCode' => 'AL'
            ]
        ]
    ]);

I can then capture the errors as follows:
if ( ! $requestModel->validate())
{
    Craft::dd($requestModel->getErrors());
}

It works, but is there a better way?

**Tags:** plugin-development, models, attribute


---

## 1 answers

### Answer by username_1 on 2015-04-07. Score: 2 | Accepted answer

In your Micros_GuestRegistrationRequestModel's attribute definition, if you define person like so:
'person' => array(AttributeType::Mixed, 'model' => 'Micros_GuestPersonModel');

Craft should take care of ensuring person will be an instance of Micros_GuestPersonModel.

> **Comments:**
> **username_0**: Great, thanks. Are there any validation helpers or is my validatePerson() method above the way to go?  
> **username_1**: Honestly, I can't recall if calling validate on the main model will run validation for attributes that have a model class. Would be easy to test, though.  
> **username_0**: Hmmm, doesn't seem to work for me - I can pass anything into the attribute and all it seems to do is ensure something has been passed (required). It would be great if a model's validate method could also validate attribute models too at some point. I'll add that in the form of a custom validator for now.  
> **username_1**: Yeah, custom validation is the way to go for now, then. You can always add a [feature request](http://feedback.buildwithcraft.com/)!  
> **username_0**: Done! Thanks Brad. Setting a model is pretty neat - I can see that if I pass an array it populates the model for me automatically, which saves some boilerplate :-)  

---

