Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Comprehensive Validation with Laravel 4

4,018 views

Published on

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

Published in: Software
  • Login to see the comments

Comprehensive Validation with Laravel 4

  1. 1. COMPREHENSIVE VALIDATION WITH LARAVEL 4 Kirk Bushell
  2. 2. 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
  3. 3. “ALWAYS PASS ON WHAT YOU LEARN”
  4. 4. “ALWAYS PASS ON WHAT YOU LEARN” - YODA
  5. 5. WHY CARE ABOUT VALIDATION?
  6. 6. WHY CARE ABOUT VALIDATION? ● Validation can get messy - really quick
  7. 7. WHY CARE ABOUT VALIDATION? ● Validation can get messy - really quick ● We do it. All the time.
  8. 8. WHY CARE ABOUT VALIDATION? ● Validation can get messy - really quick ● We do it. All the time. ● Lots of architectural discussion in the community
  9. 9. INSPIRATION ● What started this thought process?
  10. 10. INSPIRATION ● What started this thought process? ● Jeffrey Way’s twitter post earlier this year about where people put their validation rules.
  11. 11. 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
  12. 12. 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.
  13. 13. BUT FIRST, A FEW POINTS
  14. 14. BUT FIRST, A FEW POINTS ● This approach is best suited to medium-large applications
  15. 15. 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
  16. 16. 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
  17. 17. 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)
  18. 18. WHAT WILL WE COVER
  19. 19. WHAT WILL WE COVER ● A brief history of MVC (to provide context)
  20. 20. WHAT WILL WE COVER ● A brief history of MVC (to provide context) ● Good validation practice (resource vs use-case)
  21. 21. 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
  22. 22. 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?)
  23. 23. 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
  24. 24. ASSUMPTIONS ● You know a thing or two about Laravel 4’s validation functionality
  25. 25. ASSUMPTIONS ● You know a thing or two about Laravel 4’s validation functionality ● You understand how to use Laravel’s IoC features
  26. 26. 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)
  27. 27. 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)
  28. 28. A BRIEF HISTORY OF MVC
  29. 29. A BRIEF HISTORY OF MVC ● No one knew
  30. 30. A BRIEF HISTORY OF MVC ● No one knew ● Fat controllers
  31. 31. A BRIEF HISTORY OF MVC ● No one knew ● Fat controllers ● Skinny controllers, fat models
  32. 32. A BRIEF HISTORY OF MVC ● No one knew ● Fat controllers ● Skinny controllers, fat models ● Hexagonal architecture (service layers)
  33. 33. A BRIEF HISTORY OF MVC ● No one knew ● Fat controllers ● Skinny controllers, fat models ● Hexagonal architecture (service layers) ● Repositories
  34. 34. A BRIEF HISTORY OF MVC ● No one knew ● Fat controllers ● Skinny controllers, fat models ● Hexagonal architecture (service layers) ● Repositories ● Validation?
  35. 35. THE REPOSITORY PATTERN ● Why?
  36. 36. THE REPOSITORY PATTERN ● Why? ● Helped clean up models
  37. 37. THE REPOSITORY PATTERN ● Why? ● Helped clean up models ● Ensured a common interface for establishing data storage access
  38. 38. 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…
  39. 39. 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?
  40. 40. WHY NOT ON THE MODEL? ● Breaks the Single Responsibility Principle
  41. 41. WHY NOT ON THE MODEL? ● Breaks the Single Responsibility Principle ● Makes no sense if you’re using repositories
  42. 42. 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
  43. 43. 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
  44. 44. 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.
  45. 45. 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?
  46. 46. HOW I VIEW MODELS ● Our models are already a mess of various responsibilities
  47. 47. HOW I VIEW MODELS ● Our models are already a mess of various responsibilities ● What table or collection to talk to
  48. 48. HOW I VIEW MODELS ● Our models are already a mess of various responsibilities ● What table or collection to talk to ● Relationships
  49. 49. HOW I VIEW MODELS ● Our models are already a mess of various responsibilities ● What table or collection to talk to ● Relationships ● Querying
  50. 50. 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.
  51. 51. 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
  52. 52. 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(); } }
  53. 53. 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(); } }
  54. 54. 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(); } }
  55. 55. 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(); } }
  56. 56. BUT… CAN WE DO BETTER?
  57. 57. OF COURSE WE CAN
  58. 58. OF COURSE WE CAN ;)
  59. 59. A CUSTOM VALIDATOR ● Defines an approach to handle validation use-cases
  60. 60. A CUSTOM VALIDATOR ● Defines an approach to handle validation use-cases ● Easier to use and read in our code
  61. 61. 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
  62. 62. 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
  63. 63. 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
  64. 64. 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);
  65. 65. 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);
  66. 66. 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);
  67. 67. 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);
  68. 68. 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; } }
  69. 69. abstract class Validation { public function getRules() { return $this->rules; } public function getInput() { return $this->input; } }
  70. 70. 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; } }
  71. 71. 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(); }
  72. 72. 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(); }
  73. 73. 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(); }
  74. 74. A VALIDATION USE-CASE ● User registration (everyone loves registration… right?)
  75. 75. A VALIDATION USE-CASE ● User registration (everyone loves registration… right?) ● We’ll need to define a validator specific to this requirement
  76. 76. 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:
  77. 77. 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
  78. 78. 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
  79. 79. 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
  80. 80. class UserRegistrationValidation extends Validation { protected $rules = [ ‘username’ => [‘required’, ‘min:3’], ‘email’ => [‘required’, ‘email’], ‘password’ => [‘required’, ‘min:8’] ]; }
  81. 81. class UserRegistrationValidation extends Validation { public function getRules() { $rules = [ ‘username’ => [‘required’, ‘min:3’], ‘email’ => [‘required’, ‘email’], ‘password’ => [‘required’, ‘min:8’] ]; return $rules; } }
  82. 82. SO… HOW DO WE USE THIS? ● Utilise our validation for user registration
  83. 83. SO… HOW DO WE USE THIS? ● Utilise our validation for user registration ● Provide it with the required $input (in this case, probably Input::get())
  84. 84. 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
  85. 85. 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!
  86. 86. 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?
  87. 87. // UserController public function postRegister() { $input = Input::get(); try { App::make(‘UserRegistrationValidation’, [$input])->validate(); } catch (ValidationException $e) { // Handle errors } // Create a response return User::create($input); }
  88. 88. BUT WAIT...
  89. 89. THAT’S UGLY.
  90. 90. AND LARAVEL CAN HELP :)
  91. 91. EXCEPTIONS FOR DAYS ● We can use Laravel’s own error-handling to our advantage
  92. 92. EXCEPTIONS FOR DAYS ● We can use Laravel’s own error-handling to our advantage ● Automatically catch ValidationException(s)
  93. 93. EXCEPTIONS FOR DAYS ● We can use Laravel’s own error-handling to our advantage ● Automatically catch ValidationException(s) -> ○ Render a response
  94. 94. 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.
  95. 95. App::error(function(ValidationException $exception) { $errorResponse = [ ‘message’ => $exception->getMessage(), ‘errors’ => $exception->getErrors() ]; return Response::json($errorResponse, $statusCode = 422); }
  96. 96. App::error(function(ValidationException $exception) { $errorResponse = [ ‘message’ => $exception->getMessage(), ‘errors’ => $exception->getErrors() ]; return Response::json($errorResponse, $statusCode = 422); }
  97. 97. App::error(function(ValidationException $exception) { $errorResponse = [ ‘message’ => $exception->getMessage(), ‘errors’ => $exception->getErrors() ]; return Response::json($errorResponse, $statusCode = 422); }
  98. 98. App::error(function(ValidationException $exception) { $errorResponse = [ ‘message’ => $exception->getMessage(), ‘errors’ => $exception->getErrors() ]; return Response::json($errorResponse, $statusCode = 422); }
  99. 99. TO CONCLUDE ● We’ve setup validation as part of its own domain (it’s entirely responsible for nothing other than validation)
  100. 100. 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.
  101. 101. 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!
  102. 102. 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)
  103. 103. // Instead of this... public function postRegister() { $input = Input::get(); try { App::make(‘UserRegistrationValidation’, [$input])->validate(); } catch (ValidationException $e) { // Handle errors } // Create a response return User::create($input); }
  104. 104. // Now we have this. public function postRegister() { $input = Input::get(); App::make(‘UserRegistrationValidation’, [$input])->validate(); return User::create($input); }
  105. 105. A FINAL POINT
  106. 106. A FINAL POINT ● I’m still learning
  107. 107. A FINAL POINT ● I’m still learning ● Mitchell’s talk on Doctrine
  108. 108. A FINAL POINT ● I’m still learning ● Mitchell’s talk on Doctrine ● Value objects could be interesting for validation requirements (Mathias?)
  109. 109. THANK YOU :)
  110. 110. THANK YOU :) ● http://kirkbushell.me ● https://github.com/kirkbushell ● Talk comments/feedback: https://joind.in/talk/view/11690 ● @kirkbushell

×