在Laravel 中,有很多方法可以验证传入的数据,对于表单请求,通常主要有两种方式:
在控制器中使用ValidatesRequests
的 validate()
方法
创建表单验证类
对于第一种方式,只适用一些功能单一、验证规则比较简单的验证场景。
对于复杂一些的验证场景,使用表单验证,会更方便一些。
经常使用表单验证的同学可能会知道,Request 类也不会万能的,对于一些重复使用的验证规则,默认的Request 类,并没有提供好的验证规则复用方法。
所以有没有某种方案,最终可以解决以下需求:
rules()
方法只需要返回一个该请求的验证规则数组
基于路由场景验证,不同的验证场景可以使用相同的验证规则
对于字段相同,但是验证规则不同的情况,可以重置验证规则
感谢 sirping 的 Laravel 验证类 实现 路由场景验证 和 控制器场景验证 ,提供了一个基于路由的场景验证的简单易用方案。
因为每一个Request 类,后面都会使用到场景验证,所以这里直接创建一个基类继承于 FormRequest
类,并重写相关方法:
app/Http/Requests/BaseRequest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 <?php namespace App \Http \Requests ;use App \Traits \ApiResponse ;use Illuminate \Contracts \Validation \Factory as ValidationFactory ;use Illuminate \Contracts \Validation \Validator ;use Illuminate \Foundation \Http \FormRequest ;use Illuminate \Http \Exceptions \HttpResponseException ;class BaseRequest extends FormRequest { use ApiResponse ; protected $scene = null ; protected $autoValidate = true ; protected $onlyRule=[]; public function authorize () { return true ; } public function validateResolved () { if (method_exists($this , 'autoValidate' )) { $this ->autoValidate = $this ->container->call([$this , 'autoValidate' ]); } if ($this ->autoValidate) { $this ->handleValidate(); } } protected function handleValidate () { $this ->prepareForValidation(); if (! $this ->passesAuthorization()) { $this ->failedAuthorization(); } $instance = $this ->getValidatorInstance(); if ($instance->fails()) { $this ->failedValidation($instance); } } public function validator ($factory) { return $factory->make($this ->validationData(), $this ->getRules(), $this ->messages(), $this ->attributes()); } public function validate ($scene = '' ) { if (!$this ->autoValidate) { if (is_array($scene)) { $this ->onlyRule = $scene; } else { $this ->scene = $scene; } $this ->handleValidate(); } } protected function getRules () { return $this ->handleScene($this ->container->call([$this , 'rules' ])); } protected function handleScene (array $rule) { if ($this ->onlyRule) { return $this ->handleRule($this ->onlyRule, $rule); } $sceneName = $this ->getSceneName(); if ($sceneName && method_exists($this , 'scene' )) { $scene = $this ->container->call([$this , 'scene' ]); if (array_key_exists($sceneName, $scene)) { return $this ->handleRule($scene[$sceneName], $rule); } } return $rule; } private function handleRule (array $sceneRule, array $rule) { $rules = []; foreach ($sceneRule as $key => $value) { if (is_numeric($key) && array_key_exists($value, $rule)) { $rules[$value] = $rule[$value]; } else { $rules[$key] = $value; } } return $rules; } protected function getSceneName () { return is_null($this ->scene) ? $this ->route()->getAction('_scene' ) : $this ->scene; } protected function failedValidation (Validator $validator) { throw new HttpResponseException( $this ->failed($validator->errors()->first()) ); } }
其中,ApiResponse
是一个封装了返回客户端内容的 Trait。
添加路由场景方法 然后在 app\Providers\AppServiceProvider.php
类中的 boot()
方法中添加场景方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php namespace App \Providers ;use Illuminate \Routing \Route ;use Illuminate \Support \ServiceProvider ;class AppServiceProvider extends ServiceProvider { public function boot () { Route::macro('scene' , function ($scene = null) { $action = Route::getAction(); $action['_scene' ] = $scene; Route::setAction($action); }); } }
使用路由场景方法 该自定义方法用于路由场景验证,在 Route->action
增加一个 _scene
属性。其实用法和路由别名函数是一样的:
1 Route::post('add' ,'UserController@add' )->scene('add' );
路由场景验证 UserRequest
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php namespace App \Http \Requests ;use Illuminate \Foundation \Http \BaseRequest ;class UserRequest extends BaseRequest { public function authorize () { return true ; } public function rules () { return [ 'name' => 'required|string|unique:users' , 'email' => 'required|email|unique:users' , ]; } public function scene () { return [ 'add' => [ 'name' , 'email' => 'email|unique:users' ], 'edit' => ['name' ], ] } }
scene()
方法中的场景,不是控制器的方法名称,而是需要通过路由去自定义,可以使任意合法的名称,不一定要与控制器方法名保持一致。
至此就完成了上面提到的三个需求,使用起来也比较简单,没有破坏框架原本用法。
参考链接