Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T20:03:07.630Z
Answers.Text: Great, thanks. Are there any validation helpers or is my validatePerson() method above the way to go?
Answers.UserDisplayName: Russ Back
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T20:06:08.933Z
Answers.Text: 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.
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T20:24:25.933Z
Answers.Text: 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.
Answers.UserDisplayName: Russ Back
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T20:32:44.197Z
Answers.Text: Yeah, custom validation is the way to go for now, then. You can always add a [feature request](http://feedback.buildwithcraft.com/)!
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T20:36:57.167Z
Answers.Text: 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 :-)
Answers.UserDisplayName: Russ Back
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-04-07T18:57:10.087Z
Answers.LastActivityDate: 2015-04-07T18:57:10.087Z
Answers.UserDisplayName: Brad Bell
Body: <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>
  
Tags: <plugin-development><models><attribute>
Title: Is there a good way of setting a model attribute to be an instance of another model?
ThreadText: # 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 username_1. 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 :-)  
  
  ---
  
  