Jan 13, 2014
Table of contents:
All applications require validation in some form or another. Creating your validation code as a service is a fantastic way to implement validation because it keeps that part of your application focused on one specific job.
If you have been following along with the “Building Cribbb” series you will have seen me go from using Ardent for auto-hydrating models, to creating Validation services and then to extending Laravel’s eloquent to create Magniloquent.
I think handling validation in the model is a fine solution in the case of a lot of types of application. When your application is small and unlikely to go through radical change in the future, model validation makes a lot of sense because it keeps a lot of the code together.
However for Cribbb, I want to make the application as robust as possible. For this reason I’ve decided to fully implement validation as a service rather than using Magniloquent. I love what Magniloquent has come to be (shout out to Alex Sears) but I feel like validation as a service is the right path to take for Cribbb.
So this is how I have created my validation as a service set up.
The following post was heavily influenced by Chris Fidao’s book Implementing Laravel. If you are looking to learn more about advanced structure of web applications I would highly recommend grabbing a copy!
Before I start writing code for something like this I like to get the plan right in my head first.
For this I usually just write out
What follows is the step-by-step process I worked through to create my validation as a service.
So the first thing to do is to create the directory structure that will hold my code. As with all of the rest of my application specific code that doesn’t warrant creating a separate package, this code will live under the Cribbb
namespace and directory in my application structure:
<?php namespace Cribbb\Service\Validation;
So as you can see, I like to keep all of my services under the service
namespace and then I give each individual service it’s own namespace.
Next I need to think of the specific implementations of the validation service. I’m going to be using the Laravel validation class but I don’t want to tie myself to it forever because switching to the hot new thing would be painful.
In reality it’s unlikely that I’m going to change my validation implementation, but structuring your code to allow for this possibility is an excellent practice to adopt.
So I will be keeping all of my service classes under the Cribbb\Service\Validation
namespace. I will then create a namespace for each implementation that is nested within this directory, for example:
<?php namespace Cribbb\Service\Validation\Laravel;
The next step is to create the interface contracts I will be implementing to ensure that my code has a consistent API. A PHP interface class is essentially a contract that states that the instance of the class you are working with has the methods that are defined in the contract. This will ensure that I can switch out the various implementations of my validation code but I will always have the methods I require at hand and whenever I need to use validation in my code I can ensure it is a valid implementation of my validation service by type hinting for this interface.
Here is my interface:
<?php namespace Cribbb\Service\Validation;
interface ValidableInterface
{
/**
* With
*
* @param array
* @return self
*/
public function with(array $input);
/**
* Passes
*
* @return boolean
*/
public function passes();
/**
* Errors
*
* @return array
*/
public function errors();
}
So as you can see, by using this contract, the class in question must have the with
, passes
and errors
methods.
Why these three methods? Well the single responsibility of the validation service is to answer the question, is this data valid?
The with
method will ensure that I define a consistent approach to passing the data to the validation class. The passes
method will ensure that I have a single way to test the validity of the data, and the errors
method will ensure that I have a consistent way to return any errors back up the chain.
So this interface is a contract that states how data is passed to the class, how it is tested and how errors are returned. Nice, clean and simple.
With this validation service, a lot of the boiler plate code can be abstracted away from the implementation. The only thing that is going to be different for each implementation is how the passes
method actually validates the data against the defined rules.
This means we can create a nearly fully implemented abstract class to handle a lot of the details.
<?php namespace Cribbb\Service\Validation;
abstract class AbstractValidator
{
}
The first thing to do is to define the properties of the class:
/**
* Validator
*
* @var object
*/
protected $validator;
/**
* Data to be validated
*
* @var array
*/
protected $data = array();
/**
* Validation Rules
*
* @var array
*/
protected $rules = array();
/**
* Validation errors
*
* @var array
*/
protected $errors = array();
As you can see, we will need the following properties available:
$validator
- This will be an instance of the specific validation class that will be performing the validation tests. $data
- This will be the raw data that we pass into the validator to see if it passes our validation rules. $rules
- I will store the rules of the validation as an array of attributes. $errors
- And finally, if the validator class returns any errors they will be stored in this property.
Next we can create the with
and errors
method because these are essentially just setting and getting data on the class:
/**
* Set data to validate
*
* @param array $data
* @return self
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
The with
method accepts an array of data and returns an instance of the class. We will use this returned instance to chain methods when we come to actually implement the code.
The errors
method will simply return the $this->errors
property:
/**
* Return errors
*
* @return array
*/
public function errors()
{
return $this->errors;
}
Finally we can define the passes
method as being abstract. This means we expect the implementation of this method to be handled by a class that extends this abstract class:
/**
* Pass the data and the rules to the validator
*
* @return boolean
*/
abstract function passes();
The next thing to do is to use the boiler plate abstract class and interface to create a Laravel specific implementation. In order to do this I’m going to make an abstract LaravelValidator
which will implement the passes
method using the Laravel Validation class. I will then extend this base class for each set of rules I want for the different entities in my application.
So the first thing to do is to create the LaravelValidator
class:
<?php namespace Cribbb\Service\Validation\Laravel;
use Illuminate\Validation\Factory;
use Cribbb\Service\Validation\ValidableInterface;
use Cribbb\Service\Validation\AbstractValidator;
abstract class LaravelValidator extends AbstractValidator
{
/**
* Validator
*
* @var Illuminate\Validation\Factory
*/
protected $validator;
/**
* Construct
*
* @param Illuminate\Validation\Factory $validator
*/
public function __construct(Factory $validator)
{
$this->validator = $validator;
}
/**
* Pass the data and the rules to the validator
*
* @return boolean
*/
public function passes()
{
$validator = $this->validator->make($this->data, $this->rules);
if ($validator->fails()) {
$this->errors = $validator->messages();
return false;
}
return true;
}
}
This class extends the base AbstractValidator
class. This means we inherit all of the base functionality and we are required to fulfil the requirements of the contract.
In the construct
method I will inject an instance of the Laravel Validation class and set it on the $validator
class property.
Finally I can use the $validator
instance to fulfil the obligation of the passes
method. This isolates the Laravel specific code to this one class.
As you can see in the passes
method, I’m passing the $this->rules
property to the Laravel Validation class. This property defines the rules that determine the validity of the data but we are yet to define them.
For each set of rules that I will require in my application, I will create a class that extends this Laravel base abstract class.
For example, for creating a new user, I might require the following rules:
<?php namespace Cribbb\Service\Validation\Laravel;
use Cribbb\Service\Validation\ValidableInterface;
class UserCreateValidator extends LaravelValidator implements ValidableInterface
{
/**
* Validation for creating a new User
*
* @var array
*/
protected $rules = [
"username" => "required|min:2",
"email" => "required|email",
"password" => "required",
];
}
So as you can see, the UserCreateValidator
class inherits the base functionality of the AbstractValidator
class, the Laravel specific functionality for running validation tests from LaravelValidator
and finally each child class finishes the implementation off with the specific required rules. I’m also implementing the ValidableInterface
which ensures that this class meets the requirements of the contract.
Finally I can write some tests to ensure that the code is working correctly.
In order to write tests for my validation service, I will create a StubValidator
class that I will use only for my tests. If you don’t want this class lying around you can of course use one of your real implementations, however, you will probably end up rewriting your tests whenever you change your validation rules.
Here is my StubValidator
class:
<?php namespace Cribbb\Service\Validation\Laravel;
use Cribbb\Service\Validation\ValidableInterface;
class StubValidator extends LaravelValidator implements ValidableInterface
{
/**
* Validation stub for testing
*
* @var array
*/
protected $rules = [
"username" => "required|alpha_dash|min:2",
"email" => "required|email",
"password" => "required|confirmed",
];
}
First I will unit test the service. Remember, a unit test is the smallest bit of functionality. We are not concerned with any external dependencies at this point:
use Mockery as m;
use Cribbb\Service\Validation\Laravel\StubValidator;
class ValidatorTest extends TestCase
{
public function tearDown()
{
m::close();
}
/**
* @expectedException Exception
*/
public function testValidatorThrowsExceptionOnWrongDependency()
{
$validator = new StubValidator(new StdClass());
}
/**
* @expectedException Exception
*/
public function testWithMethodThrowsExceptionIfNotArray()
{
$validator = new StubValidator(
m::mock("Illuminate\Validation\Factory")
);
$validator->with("hello world");
}
/**
* @expectedException Exception
*/
public function testPassesMethodThrowsExceptionIfNotArray()
{
$validator = new StubValidator(
m::mock("Illuminate\Validation\Factory")
);
$validator->passes("hello world");
}
}
Here I’m simply ensuring that the class will only accept the correct input. The first test ensures that the class will only accept a class of the correct instance, and the other two tests ensure that the with
and passes
methods will only accept arrays.
Next I can write integration tests to ensure that my service is working correctly:
use Cribbb\Service\Validation\Laravel\StubValidator;
class UserValidatorTest extends TestCase
{
public function testCreateSuccess()
{
$stub = new StubValidator(App::make("validator"));
$this->assertTrue($stub->with($this->getValidCreateData())->passes());
}
public function testCreateFailure()
{
$stub = new StubValidator(App::make("validator"));
$this->assertFalse(
$stub->with($this->getInvalidCreateData())->passes()
);
$this->assertEquals(3, count($stub->errors()));
$this->assertInstanceOf(
"Illuminate\Support\MessageBag",
$stub->errors()
);
}
public function getValidCreateData()
{
return [
"username" => "philipbrown",
"email" => "name@domain.com",
"password" => "totes_secure_password",
"password_confirmation" => "totes_secure_password",
];
}
public function getInvalidCreateData()
{
return [
"username" => "<@)}}}><{",
"email" => "this is not an email",
"password" => "totes_secure_password",
"password_confirmation" => "blah_blah_blah",
];
}
}
In these tests I’m simply asserting that I’m getting the correct responses from the methods.
And there you have it, a pretty nice implementation of validation as a service. We have abstracted the validation component of our application into it’s own service that we can grab whenever we need it. What’s more, we’ve created a specific implementation for Laravel so we are not tied to one validation library and we’ve structured our code to make it really easy to have many different rules for our models for creating or updating them with independent requirements.
As I mentioned in the introduction, this post was highly inspired by the fantastic book Implementing Laravel by Chris Fidao. If you are looking for some advanced Laravel material and excellent guidance on writing maintainable and flexible code, I would highly recommend Chris’ book. Chris also has some fantastic tutorials on his blog so you should check them out too!
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.