When I was first started playing around with NodeJS few months back, I came across Mongoose, a really neat ODM for MongoDB. You just needed to define the schema for each entity and pass it to that model and Mongoose would validate all data against that schema. That concept kinda intrigued me, as you wouldn’t need to create your own validation for the data.
For a couple of months now, I’ve been playing around with Laravel and it has an excellent ORM called Eloquent. Now as far as creating a model goes, it is simple, easy to use. But no validation against a schema or set of rules. So far I’ve been using a Validation service that I’d create & its sub-classes (one for each model with model specific rules). But I’ve been thinking why not do this inside the model itself?
So the base model would be something like (not exact code):
class BaseModel extends Eloquent {
protected $_rules = array(
'create' => array(),
'update' => array(),
);
protected $_custom_errors = array();
protected function _validate( $data, $rules ) {
$validation = Validator::make( $data, $rules, $this->_custom_errors );
if ( $validation->fails() ) {
//data validation failed, throw an exception
//ValidationException is a custom exception which
//can accept MessageBag instance as first argument
throw new ValidationException( $validation->messages() );
}
//all good & hunky dory
return true;
}
public function create( array $attributes ) {
try {
$this->_validate( $attributes, $this->_rules['create'] );
} catch ( ValidationException $e ) {
//we would want to catch it in controller
//its of no use here, so lets re-throw
//the exception
throw $e;
}
//all good
return parent::create( $attributes );
}
}
and then each model can extend this BaseModel and define their individual rules for data validation:
class User extends BaseModel {
protected $_rules = array(
'create' => array(
'username' => array(
'required', 'unique', 'min:8', 'max:20',
),
'password' => array(
'required', 'min:8', 'confirmed',
),
'password_confirmation' => array(
'required', 'min:8',
),
),
'update' => array(
'password' => array(
'min:8', 'confirmed',
),
'password_confirmation' => array(
'min:8',
),
),
);
}
and that would be it. Now if a new user is to be created then all that will need to be done is:
class UsersController extends BaseController {
public function create() {
try {
User::create( Input:all() );
} catch ( ValidationException $e ) {
//Ooo, exception caught
//do something about it
return Redirect::back()
->withInput( Input:except( 'password', 'password_confirmation' ) )
->withErrors( $e->get_errors() );
}
//all good, user created
return Redirect::back()
->with( 'message', 'Registration Successful' );
}
}
This would be a good way of doing things. A couple of days back I came across 2 packages – Ardent & Magniloquent. Both of these are layers that sit on top of Eloquent do the same thing (Magniloquent was inspired by Ardent but is a bit more super-charged), they make models super-charged ie., you define validation rules in your models and they take care of validation before data is saved. The code would be streamlined just like I’ve shown above.
But is this the right way to do things? A model’s role is to act as a bridge between app and data source, to make data available. Data validation is not its job. What happens to Single Responsibility?
There seem to be more than one school of thoughts on this one, some advocate this approach while some don’t. I guess in the end it comes down to personal preference and which school of thought you subscribe to.