cult3

How to create an Active Record style PHP SDK Part 5

Aug 06, 2014

Table of contents:

  1. Why is validation important?
  2. Using a validation library
  3. Writing the tests
  4. Creating the Validating trait
  5. Conclusion

I think it goes without saying that validation is an extremely important aspect of writing high quality applications. Validation enables us to protect the sanity of the data that is allowed across the boundary from the user to the application.

Whilst we can all agree that validation is important, where it actually happens is still up for debate. Ruby on Rail’s ActiveModel is probably the most well known Active Record implementation that encourages you to perform validation in the model, whilst other strong proponents will say validation is not the concern of the model.

In today’s tutorial we’re going to be looking at implementing validation for our Active Record style PHP SDK.

Why is validation important?

It’s not uncommon to have multiple forms of validation throughout a single application. Typically you will have client-side validation, server side validation and perhaps even business rules that restrict how data should enter your application.

When working with a third-party API, you will often find that they enforce a number of validation rules around the data you are allowed to send. Typically if you send a request that does not satisfy the validation requirements you will be returned a 4XX response and a description of what you did wrong.

I think one of the nicer aspects of Active Record pattern SDKs is when the model can validate itself. The rules around what is considered valid and what is considered invalid for a third-party API is definitely the responsibility of the model.

By using validation with a third-party API we can also ensure that all requests have the correct data before we contact the API. This will save the round trip of sending invalid data to the API and then parsing the error response.

In today’s tutorial we’ll be looking at implementing validation in our models.

Using a validation library

Validation is going to be an important component of this package, but I definitely do not want to start writing my own validation package.

Instead we can turn to the beautiful world of Open Source to see what we have available.

illuminate\validation

The first package I looked at was the validation package of Laravel, illuminate\validation.

I already know this package pretty well after working with it a lot whilst using Laravel. I really like that I can specify the rules to validate against as an array. This will fit really nicely with how I want to define my model rules.

However, if we look at the composer.json I’m not very keen on how many dependencies this package has. I don’t want to pull in a load of Laravel stuff into this package just so I can validate my models.

respect\validation

The next package I looked at was respect\validation.

This seems like a really nice, thorough validation package with an impressive amount of rules.

I also really like the syntax and how you can chain together validation checks and create validator instances.

However I’d much prefer to be able to define my validation rules as a simply array so I can have it set as a property on the model. Unfortunately I can’t see a way of doing it with this package.

siriusphp\validation

The final validation package I looked at was siriusphp\validation.

I hadn’t heard of this package before doing this research, but it seems to tick all the boxes.

Firstly it doesn’t have a load of unrelated dependencies and so we will only need to pull in this one package to satisfy the relatively small job of validation.

Secondly you can define your validation rules as an array. This will be perfect for defining the model rules on each model instance.

Add the following line to your composer.json file to include this validation package into your project:

"siriusphp/validation": "1.2.1"

Writing the tests

The first thing I’m going to do is write the tests for the validation of models:

public function testValidatingFailsWithMissingRequiredEmail()
{
    $this->assertFalse($this->model->validate());
}

public function testValidatingPassesWithRequiredEmail()
{
    $this->model->email = 'name@domain.com';
    $this->assertTrue($this->model->validate());
}

I’m including these two tests in the ModelTest.php test file.

The first test is checking to ensure that the model is not valid because we haven’t included the required email property.

The second test will ensure that the validation is working by returning true when we do set the email property.

Finally I will define the $rules property on the ModelStub:

class ModelStub extends PhilipBrown\CapsuleCRM\Model
{
    protected $fillable = ["name", "email"];

    protected $rules = ["email" => "required"];

    public function __construct(Connection $connection, $attributes = [])
    {
        parent::__construct($connection);

        $this->fill($attributes);
    }
}

If you now run these tests you should see a whole lotta red. Now we’ll implement the code to get it working.

Creating the Validating trait

Validation is one of those things that is likely really critical in certain models, but probably not needed at all in others. Not every resource of an API will have validation rules, and so having the validation logic in each model seems like a bit of a waste.

To get around this problem we can simply define a Validating trait that can be included in the relevant models:

<?php namespace PhilipBrown\CapsuleCRM;

trait Validating
{
}

As you can see from the tests above, whenever we want to validate the properties of the model, we should be able to call a public method of validate.

So firstly we can define this method:

/**
 * Validate the model's properties
 *
 * @return bool
 */
public function validate()
{

}

Next we can import the validation library that we pulled in through Composer and create a new instance within the validate() method:

<?php namespace PhilipBrown\CapsuleCRM;

use Sirius\Validation\Validator;

trait Validating
{
    /**
     * Validate the model's properties
     *
     * @return bool
     */
    public function validate()
    {
        $validator = new Validator();
    }
}

We’re never going to need to mock the validator so I think it’s fine to just instantiate a new instance within the method.

Next we can add the $this->rules property to the validator. This property will hold an array of validation rules for the current model:

$validator->add($this->rules);

Next we can attempt to validate the $this->attributes. If you remember from a couple of weeks, the $this->attributes will hold the properties of the model:

if ($validator->validate($this->attributes)) {
    return true;
}

If the validation passes we can simply return true.

However if the validation does not pass we can set the $this->errors property so we can make them available outside of the model. We can also return false to signify that the validation failed:

$this->errors = $validator->getMessages();

return false;

Finally add an $errors class property to hold the errors:

/**
 * The validation errors
 *
 * @var array
 */
protected $errors;

And a public method to return the error messages outside of the model:

/**
 * Return the validation errors
 *
 * @return array
 */
public function errors()
{
    return $this->errors;
}

Now if we run the tests again everything should work correctly!

Conclusion

By implementing validation directly on our model instances we can ensure that requests to the API are more likely to be accepted. Dealing with errors from the API for silly mistakes like a missing property can be easily prevented with simple validation. This will make it much easier for developers to consume this package in their projects and deal with making requests gracefully.

In this tutorial we’ve created a reusable trait that can be simply added to any model instance. Now for each model that requires validation we can define an array of rules that must be satisfied.

Remember, if you would like to grab a copy of the code that we’re writing for this creating an Active Record style PHP SDK mini-series, you can find it on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.