1

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?

2

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.

| improve this answer | |
  • Great, thanks. Are there any validation helpers or is my validatePerson() method above the way to go? – Russ Back Apr 7 '15 at 20:03
  • 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 Apr 7 '15 at 20:06
  • 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 Apr 7 '15 at 20:24
  • Yeah, custom validation is the way to go for now, then. You can always add a feature request! – Brad Bell Apr 7 '15 at 20:32
  • 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 Apr 7 '15 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.