A couple of weeks ago I wrote a tutorial on using Ardent with Laravel 4. Whilst Ardent has some very nice features, it does have a couple of limitations too.

I’ve decided to drop Ardent and instead build out my own Validation Services and extension to Eloquent. Over the next couple of posts I’ll walk you through doing just that.

Whilst for the majority of the time I love using packages like Ardent, sometimes you just have to write something yourself for it to work exactly how you want it to.

If you are happy using Ardent, hopefully these next couple of posts will be a good example of how to extend Laravel features or create your own features that leverage Laravel’s extensibility.

In this post I’m going to look at creating Validation Services.

The problem with Validation

As I mentioned in Setting up your first Laravel 4 Model, a common problem with Validation in applications is that it is handled in the wrong place. Often you will see Validation handled in the Controller.

This is bad for a number of reasons. The main reason is, you are likely going to repeat yourself at some point and so it violates the principle of Don’t Repeat Yourself.

Instead, you should abstract your Validation to your Model, or in this case to a Service. This means should you want to update your validation rules, you don’t have to go hunting through your code to find every instance that you want to change.

Validation as a Service

In this post I’m going to look at creating Validation Services. You can simply think of a Service as a small internal package.

Because this is an internal package that is related specific to Cribbb, I’m going to keep all the files together in the lib/Cribbb directory that I created when I built the User Repository stuff.

So create a new folder structure under lib/Cribbb called Services/Validators.

Abstract Validator

This Validation Service is going to have one abstract class that defines the main methods that I’m going to need, and many individual classes that have the Model specific rules for validating. By extending the abstract class, it means we can abstract all of the methods into one place.

An abstract class simply means it shouldn’t be instantiated by itself.

Create a new file called Validator.php:

<?php namespace Cribbb\Services\Validators;

abstract class Validator {

protected $input;

protected $errors;

public function __construct($input = NULL)  
{  
$this->input = $input ?: \Input::all();  
}

public function passes()  
{  
$validation = \Validator::make($this->input, static::$rules);

if($validation->passes()) return true;

$this->errors = $validation->messages();

return false;  
}

public function getErrors()  
{  
return $this->errors;  
}

}  

The __construct method accepts an optional $input variable which will be what we are wanting to validate. I’ve set this as optional because the majority of the time I’ll just want to use Input::all();, but in some circumstances I might want to overwrite it.

Next, the passes() method simply creates a new instance of the Validator class. I pass in the $input and a static $rules property which will be in the individual Model’s Validator class. Next I check to see if the validation has passed and return true, otherwise I return false and set the $errors property.

Finally, the getErrors() method simply returns the $errors property because it is protected.

Specific Model Validation

Now that the abstract class has been created, I can now extend it with individual classes for each Model.

For example, here is what I would use to validate Users:

<?php namespace Cribbb\Services\Validators;

class User extends Validator {

/**  
* Validation rules  
*/  
public static $rules = array(  
‘username’ => ‘required’,  
’email’ => ‘required|email’,  
‘password’ => ‘required|min:8|confirmed’,  
‘password_confirmation’ => ‘required|min:8’,  
);

}  

As you can see, first I need to extend the Validator{} class.

Next, the only thing this class needs is a static property called $rules which will hold the validation rules. You can of course hold whatever specific properties or methods that you want to be used on this particular model within this class.

Using a Validation Service in a Controller

Next I can use the Validation Service to validate the input for the store() method of the User Controller:

/**  
* Store a newly created resource in storage.  
*  
* @return Response  
*/  
public function store()  
{  
$v = new Cribbb\Services\Validators\User;

if($v->passes())  
{  
$this->user->create($input);

return Redirect::route(‘users.index’)  
->with(‘flash’, ‘The new user has been created’);  
}

return Redirect::route(‘users.create’)  
->withInput()  
->withErrors($v->getErrors());  
}  

Here I’m simply creating a new instance of the User Validator and storing it in the $v variable. Next I can check to see if the validation passes by running the passes() method. If the validation does pass I can create the new resource using the User Repository and then redirect back to the users.index page with a flash message.

If the validation fails, I can simply redirect back to the create page. The withInput() method ensures the user’s input will remain in the input fields and the withErrors($v->getErrors()); will return the errors from the validation class and set them on the $errors property of the view.

Conclusion

This is a nice way to deal with Validation in your application. Now whenever you need to change a validation rule, or add Model specific logic to a particular validation class, you can do it all in one place.

I do like the idea of separating the validation into it’s own service as it stops the Model from getting too fat.

However, I don’t really like the idea of separating the validation and the actual action of putting something into the database. I think there is a lot more we could do if I moved this logic into the Model.

Fortunately, extending Eloquent is relatively straight forward! And so that will be the topic of next week’s post.

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.

To view a full listing of the tutorials in this series, click here.