Web application常常需要對使用者輸入的參數做驗證。
這些驗證(validation)logic該放哪裡呢?
現代框架通常會提供工具協助這件事,
以Laravel來說,使用內建的Validator可以在controller內這麼做:
$validation = Validator::make( array( 'name' => Input::get( 'name' ), 'email' => Input::get( 'email' ), ), array( 'name' => array( 'required', 'alpha_dash' ), 'email' => array( 'required', 'email' ), ) ); if ( $validation->fails() ) { $errors = $validation->messages(); }
同樣的validation logic只出現一次還好,出現在不同controller就會導致這段code duplicate。
該怎麼辦呢?
試試看放進model:
class Ball extends Eloquent { private $rules = array( 'color' => 'required|alpha|min:3', 'size' => 'required', // .. more rules here .. ); public function validate($data) { // make a new validator object $v = Validator::make($data, $this->rules); // return the result return $v->passes(); } }
controller內的code就會簡化成這樣:
$b = new Ball(); if ($b->validate(Input::all())){ // success code }else{ // failure code }
還算OK的作法…如果你能接受將validation logic視為business logic的話。
這類的code有比controller和model更適合的地方。寫一個form class即可:
class ArticleForm { protected $validationRules = [ 'title' => 'required', 'content' => 'required', ]; protected $inputData; protected $validator; public function __construct($input) { $this->inputData = $input; } public function isValid() { $this->validator = Validator::make($this->input, $this->validationRules); return $this->validator->passes(); } public function getErrors() { return $this->validator->errors(); } }
controller內直接呼叫即可,看起來readable許多:
$form = new ArticleForm( Input::all() ); if ( ! $form->isValid() ){ return Redirect::back()->with( [ 'errors' => $form->getErrors() ] ); } $article = new Article( Input::only('title', 'content', 'status') ); $article->save();
如果多個model都有form classs的話,可以將共用的code抽出來成為抽象類別:
abstract class FormModel { protected $validationRules; protected $inputData; protected $validator; public function __construct($input) { $this->inputData = $input; } public function isValid() { $this->validator = Validator::make($this->input, $this->validationRules); return $this->validator->passes(); } public function getErrors() { return $this->validator->errors(); } }
這樣原本的form class可以簡化到只剩下validation rules:
class ArticleForm extends FormModel { protected $validationRules = [ 'title' => 'required', 'content' => 'required', ]; }
甚至能寫多種validation rules給不同controller:
protected $happyArticleValidationRules; protected $angryArticleValidationRules; protected $funnyArticleValidationRules; public function isValidHappy() { $this->validator = Validator::make($this->input, $this->happyArticleValidationRules); return $this->validator->passes(); } public function isValidAngry() { $this->validator = Validator::make($this->input, $this->angryArticleValidationRules); return $this->validator->passes(); } public function isValidFunny() { $this->validator = Validator::make($this->input, $this->funnyArticleValidationRules); return $this->validator->passes(); }
實作上可以參考Laravel官方論壇原始碼。
(注意:若您是用Laravel 5,Request的封裝可以取代這件事。)
下次你的application包含大量validation logic時,不妨試試看這個作法。
(Photo via Susanne Nilsson, CC licensed)