表单验证
简介
Laravel 提供了多种不同的处理方法来对应用程序传入的数据进行验证。默认情况下,Laravel 的基底控制器类使用了 ValidatesRequests
trait,其提供了一种便利的方法来使用各种强大的验证规则验证传入的 HTTP 请求。
验证快速上手
要了解 Laravel 相关的强大验证特色,先让我们来看看一个完整的表单验证示例以及返回错误消息给用户。
定义路由
首先,我们假设在 app/Http/routes.php
文件中定义了以下路由:
// 显示一个创建博客文章的表单...
Route::get('post/create', 'PostController@create');
// 保存一个新的博客文章...
Route::post('post', 'PostController@store');
GET
路由会显示一个用于创建新博客文章的表单,POST
路由则会将新的博客文章保存到数据库。
创建控制器
接下来,让我们来看下操作这些路由的控制器。我们先让 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)
{
// 验证以及保存博客发表文章...
}
}
编写验证逻辑
现在我们准备开始填写我们的 store
逻辑方法来验证我们博客发布的新文章。检查应用程序的基底控制器 (App\Http\Controllers\Controller
) 类你会看到这个类使用了 ValidatesRequests
trait。这个 trait 在你所有的控制器里提供了方便的 validate
验证方法。
validate
方法会接收 HTTP 传入的请求以及验证的规则。如果验证通过,你的代码就可以正常的运行。若验证失败,则会抛出异常错误消息并自动将其返回给用户。在一般的 HTTP 请求下,都会生成一个重定向响应,对于 AJAX 请求则会发送 JSON 响应。
让我们接着回到 store
方法来深入理解 validate
方法:
/**
* 保存一篇新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 博客文章成功发表,将其保存到数据库...
}
如你所见,我们将本次 HTTP 请求及所需的验证规则传递至 validate
方法中。另外再提醒一次,如果验证失败,将会自动生成一个对应的响应。如果验证通过,那我们的控制器将会继续正常运行。
对于嵌套属性的提醒
如果你的 HTTP 请求中包含了「嵌套」参数,则可以在验证规则中使用「点」语法来指定他们:
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
显示验证错误
如果本次请求的参数未通过我们指定的验证规则呢?正如前面所提到的,Laravel 会自动把用户重定向到先前的位置。另外,所有的验证错误会被自动 闪存至 session。
请注意我们并不需要在 GET
路由中明确的将错误消息绑定到视图上。这是因为 Laravel 会自动检查 session 内的错误数据,如果错误存在的话,它会自动将这些错误消息绑定到视图上。因此需要的注意一点是 $errors
变量在每次请求的所有视图中都可以被使用,你可以很方便的假设 $errors
变量已被定义且进行安全地使用。$errors
变量是 Illuminate\Support\MessageBag
的实例。有关此对象的详细信息,请查阅它的文档。
所以,在我们的例子中,当验证失败时,用户将被重定向到我们的控制器 create
方法,让我们在视图中显示错误的消息:
<!-- /resources/views/post/create.blade.php -->
<h1>创建文章</h1>
@if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- 创建文章的表单 -->
自定义闪存的错误消息格式
当验证失败时,如果你想要在闪存上自定义验证的错误格式,则需在控制器中重写 formatValidationErrors
。别忘了将 Illuminate\Contracts\Validation\Validator
类引入到文件上方:
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
abstract class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
/**
* {@inheritdoc}
*/
protected function formatValidationErrors(Validator $validator)
{
return $validator->errors()->all();
}
}
AJAX 请求和验证
在这个例子中,我们使用一种传统的方式来将数据发送到应用程序上。当我们在 AJAX 的请求中使用 validate
方法时,Laravel 并不会生成一个重定向响应,而是会生成一个包含所有错误验证的 JSON 响应。这个 JSON 响应会发送一个 422 HTTP 状态码。
验证数组
要验证指定数组输入字段中的每一个 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',
]
],
其它验证的处理
手动创建验证程序
如果你不想要使用 ValidatesRequests
trait 的 validate
方法,你可以手动创建一个 validator 实例并通过 Validator::make
方法在 facade 生成一个新的 validator 实例:
<?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();
}
// 保存文章...
}
}
第一个传给 make
方法的参数是验证数据。第二个参数则是数据的验证规则。
如果请求没有通过验证,则可以使用 withErrors
方法把错误消息闪存到 session。在进行重定向之后,$errors
变 量可以在视图中自动共用,让你可以轻松地显示这些消息并返回给用户。withErrors
方法接收 validator、MessageBag
,或 PHP array
。
命名错误清单
假如在一个页面中有许多表单,你可能希望为 MessageBag
的错误命名,这可以让你获取特定表单的所有错误消息。只需在 withErrors
的第二个参数设置名称即可:
return redirect('register')
->withErrors($validator, 'login');
然后你就可以从一个 $errors
变量中,获取已命名的 MessageBag
实例:
{{ $errors->login->first('email') }}
验证后的挂勾
在验证完成之后,validator 可以让你附加返回消息。你可以更简单的做进一步的验证以及增加更多的错误消息到消息集合上。在 validator 实例使用 after
方法如下所示:
$validator = Validator::make(...);
$validator->after(function($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
if ($validator->fails()) {
//
}
表单请求验证
在更复杂的验证情境中,你可能会想要创建一个「表单请求( form request )」。表单请求是一个自定义的请求类,里面包含着验证逻辑。要创建一个表单请求类,可使用 Artisan 命令行命令 make:request
:
php artisan make:request StoreBlogPostRequest
新生成的类文件会被放在 app/Http/Requests
目录下。让我们将一些验证规则加入到 rules
方法中:
/**
* 获取适用于请求的验证规则。
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
怎样才能较好的运行验证规则呢?你所需要做的就是在控制器方法中利用类型提示传入请求。传入的请求会在控制器方法被调用前进行验证,意思就是说你不会因为验证逻辑而把控制器弄得一团糟:
/**
* 保存传入的博客文章。
*
* @param StoreBlogPostRequest $request
* @return Response
*/
public function store(StoreBlogPostRequest $request)
{
// 传入的请求是有效的...
}
如果验证失败,就会生成一个重定向响应把用户返回到先前的位置。这些错误会被闪存到 session,所以这些错误都可以被显示。如果进来的是 AJAX 请求的话,则会传回一个 HTTP 响应,其中包含了 422 状态码和验证错误的 JSON 数据。
授权表单请求
表单的请求类内包含了 authorize
方法。在这个方法中,你可以确认用户是否真的通过了授权,以便更新指定数据。比方说,有一个用户想试图去更新一篇文章的评论,你能保证他确实是这篇评论的拥有者吗?具体代码如下:
/**
* 判断用户是否有权限做出此请求。
*
* @return bool
*/
public function authorize()
{
$commentId = $this->route('comment');
return Comment::where('id', $commentId)
->where('user_id', Auth::id())->exists();
}
请注意,在上面例子中调用 route
方法。该方 法可以帮助你获取路由被调用时传入的 URI 参数,如示例中的 {comment}
参数:
Route::post('comment/{comment}');
如果 authorize
方法返回 false
,则会自动返回一个 HTTP 响应,其中包含 403 状态码,而你的控制器方法也将不会被运行。
如果你打算在应用程序的其它部分处理授权逻辑,只需从 authorize
方法返回 true
:
/**
* 判断用户是否有权限做出此请求。
*
* @return bool
*/
public function authorize()
{
return true;
}
自定义闪存的错误消息格式
如果你想要自定义验证失败时闪存到 session 的验证错误格式,可在你的基底请求 (App\Http\Requests\Request
) 中重写 formatErrors
。别忘了文件上方引入 Illuminate\Contracts\Validation\Validator
类:
/**
* {@inheritdoc}
*/
protected function formatErrors(Validator $validator)
{
return $validator->errors()->all();
}
自定义错误消息
你可以通过重写表单请求的 messages
方法来自定义错误消息。此方法必须返回一个数组,其中含有成对的属性或规则以及对应的错误消息:
/**
* 获取已定义验证规则的错误消息。
*
* @return array
*/
public function messages()
{
return [
'title.required' => '标题是必填的',
'body.required' => '消息是必填的',
];
}
处理错误消息
调用一个 Validator
实例的 errors
方法,会得到一个 Illuminate\Support\MessageBag
的实例, 里面有许多可让你操作错误消息的便利方法。
查看特定字段的第一个错误消息
如果要查看特定字段的第一个错误消息,可以使用 first
方法:
$messages = $validator->errors();
echo $messages->first('email');
查看特定字段的所有错误消息
如果你想通过指定字段来简单的获取所有消息中的一个数组,则可以使用 get
方法:
foreach ($messages->get('email') as $message) {
//
}
查看所有字段的所有错误消息
如果你想要得到所有字段的消息数组,则可以使用 all
方法:
foreach ($messages->all() as $message) {
//
}
判断特定字段是否含有错误消息
if ($messages->has('email')) {
//
}
获取格式化后的错误消息
echo $messages->first('email', '<p>:message</p>');
获取所有格式化后的错误消息
foreach ($messages->all('<li>:message</li>') as $message) {
//
}
自定义错误消息
如果有需要的话,你也可以自定义错误的验证消息来取代默认的验证消息。有几种方法可以来自定义指定的消息。首先,你需要先通过传递三个参数到 Validator::make
方法来自定义验证消息:
$messages = [
'required' => ':attribute 的字段是必要的。',
];
$validator = Validator::make($input, $rules, $messages);
在这个例子中,:attribute
占位符会被通过验证的字段实际名称所取代。除此之外,你还 可以使用其它默认字段的验证消息。例如:
$messages = [
'same' => ':attribute 和 :other 必须相同。',
'size' => ':attribute 必须是 :size。',
'between' => ':attribute 必须介于 :min - :max。',
'in' => ':attribute 必须是以下的类型之一: :values。',
];
指定自定义消息到特定 的属性
有时候你可能想要对特定的字段来自定义错误消息。只需在属性名称后加上「.」符号和指定验证的规则即可:
$messages = [
'email.required' => '我们需要知道你的 e-mail 地址!',
];
在语言包中自定义指定消息
在许多情况下,你可能希望在语言包中被指定的特定属性自定义消息不被直接传到 Validator
上。因此你可以把消息加入到 resources/lang/xx/validation.php
语言包中的 custom
数组。
'custom' => [
'email' => [
'required' => '我们需要知道你的 e-mail 地址!',
],
],
可用的验证规则
以下是所有可用的验证规则清单与功能:
Accepted Active URL After (Date) Alpha Alpha Dash Alpha Numeric Array Before (Date) Between Boolean Confirmed Date Date Format Different Digits Digits Between Dimensions (Image Files) Distinct E-Mail Exists (Database) File Filled Image (File) In In Array Integer IP Address JSON Max MIME Types MIME Type By File Extension Min Not In Numeric Present Regular Expression Required Required If Required Unless Required With Required With All Required Without Required Without All Same Size String Timezone Unique (Database) URL
accepted
验证字段值是否为 yes、on、1、或 true。这在确认「服务条款」是否同意时相当有用。
active_url
验证字段值是否为一个有效的网址,会通过 PHP 的 checkdnsrr
函数来验证。
after:date
验证字段是否是在指定日期之后。这个日期将会通过 strtotime
函数来验证。
'start_date' => 'required|date|after:tomorrow'
作为替换 strtotime
传递的日期字符串,你可以指定其它的字段来比较日期:
'finish_date' => 'required|date|after:start_date'
alpha
验证字段值是否仅包含字母字符。
alpha_dash
验证字段值是否仅包含字母、数字、破折号( - )以及下划线( _ )。
alpha_num
验证字段值是否仅包含字母、数字。
array
验证字段必须是一个 PHP array
。
before:date
验证字段是否是在指定日期之前。这个日期将会使用 PHP strtotime
函数来验证。
between:min,max
验证字段值的大小是否介于指定的 min 和 max 之间。字符串、数值或是文件大小的计算方式和 size
规则相同。
boolean
验证字段值是否能够转换为布尔值。可接受的参数为 true
、false
、1
、0
、"1"
以及 "0"
。
confirmed
验证字段值必须和 foo_confirmation
的字段值一致。例如,如果要验证的字段是 password
,就必须和输入数据里的 password_confirmation
的值保持一致。
date
验证字段值是否为有效日期,会根据 PHP 的 strtotime
函数来做验证。
date_format:format
验证字段值符合定义的日期_格式_,通过 PHP 的 date_parse_from_format
函数来验证。
different:field
验证字段值是否和指定_字段( field )_不同。
digits:value
验证字段值是否为 numeric 且长度为 value。
digits_between:min,max
验证字段值的长度是否在 min 和 max 之间。
dimensions
验证的文件必须是图片并且图片比例必须符合规则:
'avatar' => 'dimensions:min_width=100,min_height=200'
可用的规则为: min_width, max_width, min_height, max_height, width, height, ratio。
distinct
当你在验证数组的时候,你可以指定某个值必须是唯一的:
'foo.*.id' => 'distinct'
email
验证字段值是否符合 e-mail 格式。
exists:table,column
验证字段值是否存在指定的数据表中。
Exists 规则的基本使用方法
'state' => 'exists:states'
指定一个特定的字段名称
'state' => 'exists:states,abbreviation'
也可以指定更多的条件,它们会被加到「where」查询语句中:
'email' => 'exists:staff,email,account_id,1'
你也可以传递 NULL
或 NOT_NULL
至「where」语句:
'email' => 'exists:staff,email,deleted_at,NULL'
'email' => 'exists:staff,email,deleted_at,NOT_NULL'
file
必须是上传的文件。
filled
验证的字段必须带有内容。
image
验证字段文件必须为图片格式( jpeg、png、bmp、gif、 或 svg )。
in:foo,bar,...
验证字段值是否有在指定的列表里面。
integer
验证字段值是否是整数。
ip
验证字段值是否符合 IP address 的格式。
json
验证字段是否是一个有效的 JSON 字符串。
max:value
字段值必须小于或等于 value 。字符串、数值或是文件大小的计算方式和 size
规则相同。
mimetypes:text/plain,...
验证的文件必须是这些 MIME 类型中的一个:
'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'