Upload
kirk-bushell
View
2.288
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Taking basic validation rules to a more manageable, readable state by implementing architectural solutions that make our validation requirements beautiful.
Citation preview
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
● 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?
● 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
● 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
● 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
● 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
● 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?
● 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
● 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
● 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();}
}
● 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?)
● 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
● 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);
}
● 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);}
● I’m still learning
● Mitchell’s talk on Doctrine
● Value objects could be interesting for validation requirements (Mathias?)
A FINAL POINT
THANK YOU :)
● http://kirkbushell.me
● https://github.com/kirkbushell
● Talk comments/feedback: https://joind.in/talk/view/11690
● @kirkbushell