💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 表单验证 ## 简介 默认情况下,Laravel 的控制器基类使用`ValidatesRequests`Trait,它提供了一种方便的方法去使用各种强大的验证规则来验证传入的 HTTP 请求。 ## 快速验证 ### 定义路由 ``` Route::get('post/create', 'PostController@create'); Route::post('post', 'PostController@store'); ``` ### 创建路由器 ``` <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller { /** * 显示创建博客文章的表单。 * * @return Response */ public function create() { return view('post.create'); } /** * 保存一篇新的博客文章。 * * @param Request $request * @return Response */ public function store(Request $request) { // 验证并存储博客文章... } } ``` ### 编写验证器逻辑 WEB页面请求失败生成一个重定向响应,而 AJAX 请求则会发送 JSON 响应,JSON 响应会包含一个 HTTP 状态码 422。 ``` /** * 保存一篇新的博客文章。 * * @param Request $request * @return Response */ public function store(Request $request) { $validatedData = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); // 博客文章验证通过 } ``` #### 首次验证失败后停止运行 附加 bail 规则,在第一次验证失败后停止验证该属性的其他验证规则。 ``` $request->validate([ 'title' => 'bail|required|unique:posts|max:255', 'body' => 'required', ]); ``` #### 验证数组数据 在验证规则中通过「点」语法来指定这些参数 ``` $request->validate([ 'title' => 'required|unique:posts|max:255', 'author.name' => 'required', 'author.description' => 'required', ]); ``` ### 显示验证错误信息 请求未通过验证,Laravel 会自动把用户重定向到之前的位置。 所有的验证错误信息会被自动存储到 session 中。 变量 $errors 是 Illuminate\\Support\\MessageBag 实例 > 注意:`$errors`变量被`Web`中间件组提供的`Illuminate\View\Middleware\ShareErrorsFromSession`中间件绑定到视图中。**当这个中间件被应用后,在你的视图中就可以获取到`$error`变量**, 可以使一直假定`$errors`变量存在并且可以安全地使用。 ``` // 无需显示绑定错误,直接在重定向的模板显示 session 内的错误信息 <!-- /resources/views/post/create.blade.php --> <h1>创建文章</h1> @if ($errors->any()) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <!-- 创建文章表单 --> ``` Blade 模板方法 ``` @error('title') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror ``` ### 允许 null 验证 默认情况下,在 Laravel 应用的全局中间件堆栈`App\Http\Kernel`类中包含了`TrimStrings`和`ConvertEmptyStringsToNull`中间件。因此,如果你不希望验证程序将`null`值视为无效的话,那就需要将「可选」的请求字段标记为`nullable`。 ``` // 允许null值有效,添加验证规则 nullable $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]); ``` ## 验证表单请求 ### 创建表单请求验证 ``` // 创建表单请求验证,生成的目录 app/Http/Requests $ php artisan make:request StoreBlogPost ``` #### 添加验证规则 ``` /** * 获取适用于请求的验证规则。 * * @return array */ public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; } ``` #### 在控制器中使用 ``` use App\Http\Requests\StoreBlogPost; /** * 存储传入的博客文章。 * * @param StoreBlogPost $request * @return Response */ public function store(StoreBlogPost $request) { // 传入的请求通过验证... // 获取通过验证的数据... $validated = $request->validated(); } ``` #### 添加表单请求后钩子 允许在验证结果返回之前调用任何方法 ``` /** * 配置验证器实例。 * * @param \Illuminate\Validation\Validator $validator * @return void */ public function withValidator($validator) { $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); } ``` ### 表单请求授权验证 可以检查经过身份验证的用户确定其是否具有更新给定资源的权限。 ``` /** * 判断用户是否有权限做出此请求。 * * @return bool */ public function authorize() { $comment = Comment::find($this->route('comment')); return $comment && $this->user()->can('update', $comment); } ``` 由于所有的表单请求都是继承了 Laravel 中的请求基类,所以可以使用`user`方法去获取当前认证登录的用户。上述例子中对`route`方法的调用允许在被调用的路由上获取其定义的 URI 参数,譬如下面例子中的`{comment}`参数: ``` Route::post('comment/{comment}'); ``` 如果`authorize`方法返回`false`,则会自动返回一个包含 403 状态码的 HTTP 响应,也不会运行控制器的方法。 如果你打算在应用程序的其它部分处理授权逻辑,只需从`authorize`方法返回`true`: ``` /** * 判断用户是否有权限进行此请求。 * * @return bool */ public function authorize() { return true; } ``` ### 自定义错误消息 ``` /** * 获取已定义验证规则的错误消息。 * * @return array */ public function messages() { return [ 'title.required' => 'A title is required', 'body.required' => 'A message is required', ]; } ``` ### 自定义验证属性 ``` /** * 获取验证错误的自定义属性。 * * @return array */ public function attributes() { return [ 'email' => '邮箱地址', ]; } ``` ## 手动创建验证器 通过 Validator facade 上的`make`方法手动创建验证器实例,传给`make`方法的第一个参数是需要验证的数据,第二个参数则是该数据的验证规则。 如果验证失败,则可以使用`withErrors`方法把错误消息闪存到 Session 。使用这个方法进行重定向后,`$errors`变量会自动和视图共享,你可以把这些消息显示给用户。`withErrors`方法接收验证器、`MessageBag`或 PHP`Array`。 ``` <?php namespace App\Http\Controllers; use Validator; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller { /** * 保存一篇新的博客文章。 * * @param Request $request * @return Response */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); // 手动处理重定向 if ($validator->fails()) { return redirect('post/create') ->withErrors($validator) ->withInput(); } // Store the blog post... } } ``` ### 自动重定向 如果验证失败,用户将会自动重定向。在 AJAX 请求中,则会返回 JSON 格式的响应。 ``` Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ])->validate(); ``` ### 命名错误包 如果一个页面中有多个表单,可以通过给`withErrors`方法传递第二个参数命名错误包来检索特定表单的错误消息。 ``` // 然后你就可以从`$errors`变量中获取指定表单的错误消息: return redirect('register') ->withErrors($validator, 'login'); {{ $errors->login->first('email') }} ``` ### 验证后钩子 验证器还允许在验证成功之后添加回调函数,以便进行下一步的验证,甚至在消息集合中添加更多的错误消息。使用它只需在验证实例上使用`after`方法: ``` $validator = Validator::make(...); $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); if ($validator->fails()) { // } ``` ## 处理错误消息 在`Validator`实例上调用`errors`方法后,你会得到一个`Illuminate\Support\MessageBag`实例,它拥有各种方便的方法处理错误信息。自动提供给所有视图的`$ errors`变量,也是`MessageBag`类的一个实例。 #### 查看特定字段的第一个错误信息 ``` $errors = $validator->errors(); echo $errors->first('email'); ``` #### 查看特定字段的所有错误消息 ``` foreach ($errors->get('email') as $message) { // } // 如果要验证表单的数组字段,可以使用`*`来获取每个数组元素的所有错误消息 foreach ($errors->get('attachments.*') as $message) { // } ``` #### 查看所有字段的所有错误消息 ``` foreach ($errors->all() as $message) { // } ``` ### 自定义错误消息 可以使用自定义错误消息取代默认值进行验证。有几种方法可以指定自定义消息。首先,你可以将自定义消息作为第三个参数传递给`Validator::make`方法: ``` // `:attribute`占位符会被验证字段的实际名称取代。 $messages = [ 'required' => 'The :attribute field is required.', ]; $validator = Validator::make($input, $rules, $messages); // 还可以在验证消息中使用其它占位符。 $messages = [ 'same' => 'The :attribute and :other must match.', 'size' => 'The :attribute must be exactly :size.', 'between' => 'The :attribute value :input is not between :min - :max.', 'in' => 'The :attribute must be one of the following types: :values', ]; ``` #### 为给定属性指定自定义消息 ``` $messages = [ 'email.required' => 'We need to know your e-mail address!', ]; ``` #### 在语言文件中指定自定义消息 在 resources/lang/xx/validation.php 语言文件的 custom 数组中自定义消息 ``` 'custom' => [ 'email' => [ 'required' => 'We need to know your e-mail address!', ], ], ``` #### 在语言文件中指定自定义属性 在 resources/lang/xx/validation.php 语言文件的 attributes 数组自定义名称 ``` 'attributes' => [ 'email' => 'email address', ], ``` #### 在语言文件中指定自定义值 有时可能需要将验证消息的`:value`占位符替换为值的自定义文字。 ``` // 模板添加 <input type="hidden" name="payment_type" value="cc"> // 当payment type为cc时,credit card number 不能为空。 $request->validate([ 'credit_card_number' => 'required_if:payment_type,cc' ]); ``` 在 resources/lang/xx/validation.php 语言文件定义`values`数组 ``` // 当payment type 为信用卡时,credit card number不能为空。 'values' => [ 'payment_type' => [ 'cc' => '信用卡' ], ], ``` ## 可用验证规则 [参考文档](https://learnku.com/docs/laravel/5.8/validation/3899#c58a91) ## 按条件增加规则 #### 存在时则验证 ``` // email 字段只有在 $data 数组中存在才会被验证 sometimes $v = Validator::make($data, [ 'email' => 'sometimes|required|email', ]); // 验证始终存在但可能为空的字段 nullable $v = Validator::make($data, [ 'publish_at' => 'nullable|date', ]); ``` #### 复杂的条件验证 传入`sometimes`方法的第一个参数是要用来验证的字段名称。第二个参数是我们想使用的验证规则。`闭包`作为第三个参数传入,如果其返回`true`, 则额外的规则就会被加入。 ``` $v = Validator::make($data, [ 'email' => 'required|email', 'games' => 'required|numeric', ]); // 增加基于更复杂的条件逻辑的验证规则 $v->sometimes('reason', 'required|max:500', function ($input) { return $input->games >= 100; }); // 对多个字段增加条件验证 $v->sometimes(['reason', 'cost'], 'required', function ($input) { return $input->games >= 100; }); ``` > 注意:传入闭包的参数 $input 是 Illuminate\Support\Fluent 实例,可用来访问你的输入或文件对象。 ## 验证数组 ``` // 验证 photos[profile] $validator = Validator::make($request->all(), [ 'photos.profile' => 'required|image', ]); // 验证指定数组输入字段中的每一个 email 是唯一的 $validator = Validator::make($request->all(), [ 'person.*.email' => 'email|unique:users', 'person.*.first_name' => 'required_with:person.*.last_name', ]); // 在语言文件定义验证信息时使用 * 字符,为基于数组的字段使用单个验证消息 'custom' => [ 'person.*.email' => [ 'unique' => 'Each person must have a unique e-mail address', ] ], ``` ## 自定义验证规则 ### 使用规则对象 注册自定义验证规则,存放目录`app/Rules` ``` $ php artisan make:rule Uppercase ``` 规则对象包含两个方法: passes 和 message。 passes 方法接收属性值和名称,并根据属性值是否符合规则而返回 true 或 false。 message 方法应返回验证失败时应使用的验证错误消息。 ``` <?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; class Uppercase implements Rule { /** * 判断验证规则是否通过。 * * @param string $attribute * @param mixed $value * @return bool */ public function passes($attribute, $value) { return strtoupper($value) === $value; } /** * 获取验证错误消息。 * * @return string */ public function message() { return 'The :attribute must be uppercase.'; } } ``` 或者从语言文件中返回一个错误文本 ``` public function message() { return trans('validation.uppercase'); } ``` 使用自定义验证规则 ``` use App\Rules\Uppercase; $request->validate([ 'name' => ['required', 'string', new Uppercase], ]); ``` ### 使用闭包 如果在应用程序中只需要一次自定义规则的功能,则可以使用闭包而不是规则对象。 ``` $validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function ($attribute, $value, $fail) { if ($value === 'foo') { $fail($attribute.' is invalid.'); } }, ], ]); ``` ### 使用扩展 注册自定义的验证规则的另一种方法是使用`Validator`facade 中的`extend`方法。需要在 [服务容器] 中使用这个方法来注册自定义验证规则: ``` <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Validator; class AppServiceProvider extends ServiceProvider { /** * 引导任何应用程序。 * * @return void */ public function boot() { /** * $attribute:要被验证的属性名称 * $value:属性的值 * $parameters :传入验证规则的参数数组 * $validator:Validator 实列 */ Validator::extend('foo', function ($attribute, $value, $parameters, $validator) { return $value == 'foo'; }); } /** * 注册服务提供器。 * * @return void */ public function register() { // } } ``` 除了使用闭包,也可以传入类和方法到`extend`方法中: ``` Validator::extend('foo', 'FooValidator@validate'); ``` ### 定义错误消息 可以使用内联自定义消息数组或者在验证语言文件中添加条目。 错误消息应该被放到数组的第一位,而不是在只用于存放属性指定错误信息的`custom`数组内。 ``` "foo" => "Your input was invalid!", "accepted" => "The :attribute must be accepted.", // 其余的验证错误消息... ``` 为错误信息定义自定义占位符 ``` /** * 启动应用程序。 * * @return void */ public function boot() { Validator::extend(...); Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) { return str_replace(...); }); } ``` ### 隐式扩展 默认情况下, 当所要验证的属性不存在或包含由[`required`](https://learnku.com/docs/laravel/5.8/validation/3899#rule-required)规则定义的空值时,那么正常的验证规则,包括自定义扩展将不会执行。 例如,[`unique`](https://learnku.com/docs/laravel/5.8/validation/3899#rule-unique)规则将不会检验`null`值: ``` $rules = ['name' => 'unique']; $input = ['name' => null]; Validator::make($input, $rules)->passes(); // true ``` 如果要求即使为空时也要验证属性,则必须要暗示属性是必须的。要创建这样一个「隐式」扩展, 可以使用`Validator::extendImplicit()`方法: ``` Validator::extendImplicit('foo', function ($attribute, $value, $parameters, $validator) { return $value == 'foo'; }); ```