comprehensive validation with laravel 4

110
COMPREHENSIVE VALIDATION WITH LARAVEL 4 Kirk Bushell

Upload: kirk-bushell

Post on 15-Jan-2015

2.291 views

Category:

Software


1 download

DESCRIPTION

Taking basic validation rules to a more manageable, readable state by implementing architectural solutions that make our validation requirements beautiful.

TRANSCRIPT

COMPREHENSIVE VALIDATION WITH

LARAVEL 4Kirk Bushell

INTRODUCTION

● Developer - 15 years experience

● Technical lead - Tectonic Digital

● Software architect - Award Force - http://awardforce.com

● Information Technologies Coordinator - Engineers without Borders

● Technical writer - http://kirkbushell.me

● Talk comments/feedback: https://joind.in/talk/view/11690

● Github - https://github.com/kirkbushell

“ALWAYS PASS ON WHAT YOU LEARN”

“ALWAYS PASS ON WHAT YOU LEARN”

- YODA

WHY CARE ABOUT VALIDATION?

WHY CARE ABOUT VALIDATION?

● Validation can get messy - really quick

WHY CARE ABOUT VALIDATION?

● Validation can get messy - really quick

● We do it. All the time.

● Validation can get messy - really quick

● We do it. All the time.

● Lots of architectural discussion in the community

WHY CARE ABOUT VALIDATION?

● What started this thought process?

INSPIRATION

● What started this thought process?

● Jeffrey Way’s twitter post earlier this year about where people put their

validation rules.

INSPIRATION

● What started this thought process?

● Jeffrey Way’s twitter post earlier this year about where people put their

validation rules.

● Jason Lewis’ article on advanced validation: http://jasonlewis.

me/article/laravel-advanced-validation

INSPIRATION

● What started this thought process?

● Jeffrey Way’s twitter post earlier this year about where people put their

validation rules.

● Jason Lewis’ article on advanced validation: http://jasonlewis.

me/article/laravel-advanced-validation

● Lots of posts about validation on forums, twitter.etc.

INSPIRATION

BUT FIRST, A FEW POINTS

● This approach is best suited to medium-large applications

BUT FIRST, A FEW POINTS

● This approach is best suited to medium-large applications

● We’re going to use “users” as a set of use-cases to demonstrate this

approach and style to the handling of validation

BUT FIRST, A FEW POINTS

● This approach is best suited to medium-large applications

● We’re going to use “users” as a set of use-cases to demonstrate this

approach and style to the handling of validation

● There will be a little code

BUT FIRST, A FEW POINTS

● This approach is best suited to medium-large applications

● We’re going to use “users” as a set of use-cases to demonstrate this

approach and style to the handling of validation

● There will be a little code (Sorry)

BUT FIRST, A FEW POINTS

WHAT WILL WE COVER

● A brief history of MVC (to provide context)

WHAT WILL WE COVER

● A brief history of MVC (to provide context)

● Good validation practice (resource vs use-case)

WHAT WILL WE COVER

● A brief history of MVC (to provide context)

● Good validation practice (resource vs use-case)

● How to architect your validation rules so that they can grow, adhering to

SOLID design principles

WHAT WILL WE COVER

● A brief history of MVC (to provide context)

● Good validation practice (resource vs use-case)

● How to architect your validation rules so that they can grow, adhering to

SOLID design principles

● Where validation should go (controllers, models, repositories - where?)

WHAT WILL WE COVER

● A brief history of MVC (to provide context)

● Good validation practice (resource vs use-case)

● How to architect your validation rules so that they can grow, adhering to

SOLID design principles

● Where validation should go (controllers, models, repositories - where?)

● Use exceptions to alter program flow and provide greater readability

WHAT WILL WE COVER

● You know a thing or two about Laravel 4’s validation functionality

ASSUMPTIONS

● You know a thing or two about Laravel 4’s validation functionality

● You understand how to use Laravel’s IoC features

ASSUMPTIONS

● You know a thing or two about Laravel 4’s validation functionality

● You understand how to use Laravel’s IoC features

● You understand the importance of a separation of concerns (if not, we’ll

cover this a little)

ASSUMPTIONS

● You know a thing or two about Laravel 4’s validation functionality

● You understand how to use Laravel’s IoC features

● You understand the importance of a separation of concerns (if not, we’ll

cover this a little)

● You’ve dealt with growing validation concerns before (or not)

ASSUMPTIONS

A BRIEF HISTORY OF MVC

● No one knew

A BRIEF HISTORY OF MVC

● No one knew

● Fat controllers

A BRIEF HISTORY OF MVC

● No one knew

● Fat controllers

● Skinny controllers, fat models

A BRIEF HISTORY OF MVC

● No one knew

● Fat controllers

● Skinny controllers, fat models

● Hexagonal architecture (service layers)

A BRIEF HISTORY OF MVC

● No one knew

● Fat controllers

● Skinny controllers, fat models

● Hexagonal architecture (service layers)

● Repositories

A BRIEF HISTORY OF MVC

● No one knew

● Fat controllers

● Skinny controllers, fat models

● Hexagonal architecture (service layers)

● Repositories

● Validation?

A BRIEF HISTORY OF MVC

● Why?

THE REPOSITORY PATTERN

● Why?

● Helped clean up models

THE REPOSITORY PATTERN

● Why?

● Helped clean up models

● Ensured a common interface for establishing data storage access

THE REPOSITORY PATTERN

● Why?

● Helped clean up models

● Ensured a common interface for establishing data storage access

● Enabled us to easily swap out storage formats, caching mechanisms and

more…

THE REPOSITORY PATTERN

● Why?

● Helped clean up models

● Ensured a common interface for establishing data storage access

● Enabled us to easily swap out storage formats, caching mechanisms and

more…

● What about validation?

THE REPOSITORY PATTERN

● Breaks the Single Responsibility Principle

WHY NOT ON THE MODEL?

● Breaks the Single Responsibility Principle

● Makes no sense if you’re using repositories

WHY NOT ON THE MODEL?

● Breaks the Single Responsibility Principle

● Makes no sense if you’re using repositories

● Should be called as part of the service layer

WHY NOT ON THE MODEL?

● Breaks the Single Responsibility Principle

● Makes no sense if you’re using repositories

● Should be called as part of the service layer

● Validation is its own domain of logic

WHY NOT ON THE MODEL?

● Breaks the Single Responsibility Principle

● Makes no sense if you’re using repositories

● Should be called as part of the service layer

● Validation is its own domain of logic

● I don’t like model-based validation… it smells.

WHY NOT ON THE MODEL?

● Breaks the Single Responsibility Principle

● Makes no sense if you’re using repositories

● Should be called as part of the service layer

● Validation is its own domain of logic

● I don’t like model-based validation… it smells.

● But kirk… why?

WHY NOT ON THE MODEL?

● Our models are already a mess of various responsibilities

HOW I VIEW MODELS

● Our models are already a mess of various responsibilities

● What table or collection to talk to

HOW I VIEW MODELS

● Our models are already a mess of various responsibilities

● What table or collection to talk to

● Relationships

HOW I VIEW MODELS

● Our models are already a mess of various responsibilities

● What table or collection to talk to

● Relationships

● Querying

HOW I VIEW MODELS

● Our models are already a mess of various responsibilities

● What table or collection to talk to

● Relationships

● Querying

● All part of active record. Validation isn’t.

HOW I VIEW MODELS

● Our models are already a mess of various responsibilities

● What table or collection to talk to

● Relationships

● Querying

● All part of active record. Validation isn’t.

● They can (and arguably - should) be used as schema descriptors for your

application

HOW I VIEW MODELS

class User extends Eloquent{

public $rules = [‘name’ => [‘required’, ‘min:8’],‘address’ => ‘required’

];

public function save() {$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new Exception(‘Ugh, y u no provide good datums!?’);

return parent::save();}

}

class User extends Eloquent{

public $rules = [‘name’ => [‘required’, ‘min:8’],‘address’ => ‘required’

];

public function save() {$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new Exception(‘Ugh, y u no provide good datums!?’);

return parent::save();}

}

class User extends Eloquent{

public $rules = [‘name’ => [‘required’, ‘min:8’],‘address’ => ‘required’

];

public function save() {$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new Exception(‘Ugh, y u no provide good datums!?’);

return parent::save();}

}

class User extends Eloquent{

public $rules = [‘name’ => [‘required’, ‘min:8’],‘address’ => ‘required’

];

public function save() {$validator = Validator::make($this->getAttributes(), $this->rules);

if (!$validator->fails())throw new Exception(‘Ugh, y u no provide good datums!?’);

return parent::save();}

}

BUT… CAN WE DO BETTER?

OF COURSE WE CAN

OF COURSE WE CAN

;)

● Defines an approach to handle validation use-cases

A CUSTOM VALIDATOR

● Defines an approach to handle validation use-cases

● Easier to use and read in our code

A CUSTOM VALIDATOR

● Defines an approach to handle validation use-cases

● Easier to use and read in our code

● Provides the ability to automatically handle validation errors

A CUSTOM VALIDATOR

● Defines an approach to handle validation use-cases

● Easier to use and read in our code

● Provides the ability to automatically handle validation errors

● Wraps Laravel’s validator so we’re not reinventing the wheel

A CUSTOM VALIDATOR

● Defines an approach to handle validation use-cases

● Easier to use and read in our code

● Provides the ability to automatically handle validation errors

● Wraps Laravel’s validator so we’re not reinventing the wheel

● Inspired by Jason Lewis’ validator (originally based on L3):

http://jasonlewis.me/article/laravel-advanced-validation

A CUSTOM VALIDATOR

abstract class Validation{

protected $rules = [];protected $messages = [];protected $input = [];

public function __construct(array $input = []) {$this->input = $input;

}

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

abstract class Validation{

protected $rules = [];protected $messages = [];protected $input = [];

public function __construct(array $input = []) {$this->input = $input;

}

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

abstract class Validation{

protected $rules = [];protected $messages = [];protected $input = [];

public function __construct(array $input = []) {$this->input = $input;

}

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

abstract class Validation{

protected $rules = [];protected $messages = [];protected $input = [];

public function __construct(array $input = []) {$this->input = $input;

}

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getInput(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

abstract class Validation{

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

}

public function getRules() {return $this->rules;

}}

abstract class Validation{

public function getRules() {return $this->rules;

}

public function getInput() {return $this->input;

}

}

abstract class Validation{

public function validate() {$rules = $this->getRules();$validator = Validator::make($this->getAttributes(), $this->rules);

if ($validator->fails())throw new ValidationException($validator);

}

public function getRules() {return $this->rules;

}}

class ValidationException extends \Exception{

public function __construct(Validator $validator){

$this->message = 'Validation has failed, or something.';$this->validator = $validator;

}

public function getErrors(){

return $this->validator->messages();}

class ValidationException extends \Exception{

public function __construct(Validator $validator){

$this->message = 'Validation has failed, or something.';$this->validator = $validator;

}

public function getErrors(){

return $this->validator->messages();}

class ValidationException extends \Exception{

public function __construct(Validator $validator){

$this->message = 'Validation has failed, or something.';$this->validator = $validator;

}

public function getErrors(){

return $this->validator->messages();}

● User registration (everyone loves registration… right?)

A VALIDATION USE-CASE

● User registration (everyone loves registration… right?)

● We’ll need to define a validator specific to this requirement

A VALIDATION USE-CASE

● User registration (everyone loves registration… right?)

● We’ll need to define a validator specific to this requirement

● Let’s go with the usual:

A VALIDATION USE-CASE

● User registration (everyone loves registration… right?)

● We’ll need to define a validator specific to this requirement

● Let’s go with the usual:

○ Username

A VALIDATION USE-CASE

● User registration (everyone loves registration… right?)

● We’ll need to define a validator specific to this requirement

● Let’s go with the usual:

○ Username

○ Email address

A VALIDATION USE-CASE

● User registration (everyone loves registration… right?)

● We’ll need to define a validator specific to this requirement

● Let’s go with the usual:

○ Username

○ Email address

○ Password

A VALIDATION USE-CASE

class UserRegistrationValidation extends Validation{

protected $rules = [‘username’ => [‘required’, ‘min:3’],‘email’ => [‘required’, ‘email’],‘password’ => [‘required’, ‘min:8’]

];}

class UserRegistrationValidation extends Validation{

public function getRules(){

$rules = [‘username’ => [‘required’, ‘min:3’],‘email’ => [‘required’, ‘email’],‘password’ => [‘required’, ‘min:8’]

];

return $rules;}

}

● Utilise our validation for user registration

SO… HOW DO WE USE THIS?

● Utilise our validation for user registration

● Provide it with the required $input (in this case, probably Input::get())

SO… HOW DO WE USE THIS?

● Utilise our validation for user registration

● Provide it with the required $input (in this case, probably Input::get())

● Then call the validate function

SO… HOW DO WE USE THIS?

● Utilise our validation for user registration

● Provide it with the required $input (in this case, probably Input::get())

● Then call the validate function

● Handle the onslaught of errors!

SO… HOW DO WE USE THIS?

● Utilise our validation for user registration

● Provide it with the required $input (in this case, probably Input::get())

● Then call the validate function

● Handle the onslaught of errors!

● Let’s see an example, shall we?

SO… HOW DO WE USE THIS?

// UserControllerpublic function postRegister(){

$input = Input::get();

try {App::make(‘UserRegistrationValidation’, [$input])->validate();

}catch (ValidationException $e) {

// Handle errors}

// Create a responsereturn User::create($input);

}

BUT WAIT...

THAT’S UGLY.

AND LARAVEL CAN HELP :)

● We can use Laravel’s own error-handling to our advantage

EXCEPTIONS FOR DAYS

● We can use Laravel’s own error-handling to our advantage

● Automatically catch ValidationException(s)

EXCEPTIONS FOR DAYS

● We can use Laravel’s own error-handling to our advantage

● Automatically catch ValidationException(s) ->

○ Render a response

EXCEPTIONS FOR DAYS

● We can use Laravel’s own error-handling to our advantage

● Automatically catch ValidationException(s) ->

○ Render a response

○ Be more awesome…. er.

EXCEPTIONS FOR DAYS

App::error(function(ValidationException $exception){

$errorResponse = [‘message’ => $exception->getMessage(),‘errors’ => $exception->getErrors()

];

return Response::json($errorResponse, $statusCode = 422);}

App::error(function(ValidationException $exception){

$errorResponse = [‘message’ => $exception->getMessage(),‘errors’ => $exception->getErrors()

];

return Response::json($errorResponse, $statusCode = 422);}

App::error(function(ValidationException $exception){

$errorResponse = [‘message’ => $exception->getMessage(),‘errors’ => $exception->getErrors()

];

return Response::json($errorResponse, $statusCode = 422);}

App::error(function(ValidationException $exception){

$errorResponse = [‘message’ => $exception->getMessage(),‘errors’ => $exception->getErrors()

];

return Response::json($errorResponse, $statusCode = 422);}

● We’ve setup validation as part of its own domain (it’s entirely responsible

for nothing other than validation)

TO CONCLUDE

● We’ve setup validation as part of its own domain (it’s entirely responsible

for nothing other than validation)

● We’ve freed our models from the additional weight of having to handle

possibly very complex validation requirements.

TO CONCLUDE

● We’ve setup validation as part of its own domain (it’s entirely responsible

for nothing other than validation)

● We’ve freed our models from the additional weight of having to handle

possibly very complex validation requirements.

● We’ve let Laravel handle our own errors - cleaning up our code!

TO CONCLUDE

● We’ve setup validation as part of its own domain (it’s entirely responsible

for nothing other than validation)

● We’ve freed our models from the additional weight of having to handle

possibly very complex validation requirements.

● We’ve let Laravel handle our own errors - cleaning up our code!

● Our validation is now much easier to extend, and implement (and move

around)

TO CONCLUDE

// Instead of this...public function postRegister(){

$input = Input::get();

try {App::make(‘UserRegistrationValidation’, [$input])->validate();

}catch (ValidationException $e) {

// Handle errors}

// Create a responsereturn User::create($input);

}

// Now we have this.public function postRegister(){

$input = Input::get();

App::make(‘UserRegistrationValidation’, [$input])->validate();

return User::create($input);}

A FINAL POINT

● I’m still learning

A FINAL POINT

● I’m still learning

● Mitchell’s talk on Doctrine

A FINAL POINT

● I’m still learning

● Mitchell’s talk on Doctrine

● Value objects could be interesting for validation requirements (Mathias?)

A FINAL POINT

THANK YOU :)

THANK YOU :)

● http://kirkbushell.me

● https://github.com/kirkbushell

● Talk comments/feedback: https://joind.in/talk/view/11690

● @kirkbushell