Laravel 5.0 之 表单验证类 (Form Requests)

让人头痛的表单验证

本文译自 Matt Stauffer 的 系列文章 .

只要你曾经在使用 Laravel 框架的过程中试图找到有关用户输入验证的最佳实践, 你就应该了解这是一个争论最多并且几乎没有达成共识的话题. 我们可以在控制器中进行验证, 可以在单独的一个服务层进行验证, 可以在模型中进行验证, 当然还可以在 Javascript 中进行验证 (这只是一个玩笑, 谁都知道不能只依赖于客户端的验证). 但是, 哪一种做法才是最佳的呢?

Laravel 5.0 新引入的表单请求 (Form Request) 特性提供了集规范性 (差不多就是 "最佳实践" 的意思) 和便捷性 (这是比之前任何一种选择都更强大也更便捷的方式) 于一体的, 在 Laravel 中执行数据检查和验证的新手段.

说明: 本文中使用新的 view() 辅助方法代替了旧版本中的 View::make() .

Form Requests 使表单验证不再让人头痛

Laravel 5.0 带来了 Form Requests, 这是一种特殊的类型, 用于在提交表单时进行数据的检查和验证. 每个 Form Request 类至少包含一个 rules() 方法, 这个方法返回一组验证规则. 除此之外还必须包含一个 authorize() 方法, 该方法返回一个布尔值, 代表是否允许用户执行本次请求.

Laravel 会在解析 POST 路由之前自动把用户输入的信息传递给相应的表单请求, 因此我们的所有验证逻辑都可以移到独立于控制器和模型之外的 FormRequest 对象中.

开始实践: 快速创建一个 Laravel 5.0 项目

如果你还没有创建好的 Laravel 5.0 项目, 用下面的命令创建一个:

$ composer create-project laravel/laravel myProject dev-develop --prefer-dist

1. 添加路由

// app/Http/routes.phpRoute::get('/', 'FriendsController@getAddFriend');Route::post('/', 'FriendsController@postAddFriend');

2. 创建控制器

//app/Http/Controllers/FriendsController:namespace App\Http\Controllers;use App\Http\Requests\FriendFormRequest;use Illuminate\Routing\Controller;use Response;use View;class FriendsController extends Controller{	public function getAddFriend()
	{		return view('friends.add');
	}	public function postAddFriend(FriendFormRequest $request)
	{		return Response::make('Friend added!');
	}
}

3. 创建视图

<html><body>
	@foreach ($errors->all() as $error)		<p class="error">{{ $error }}</p>
	@endforeach	<form method="post">
		<label>First name</label><input name="first_name"><br>
		<label>Email address</label><input name="email_address"><br>
		<input type="submit">
	</form></body></html>

4. 创建 FormRequest

// app/http/requests/FriendFormRequest.phpnamespace App\Http\Requests;use Illuminate\Foundation\Http\FormRequest;use Response;class FriendFormRequest extends FormRequest{	public function rules()
	{		return [			'first_name' => 'required',			'email_address' => 'required|email'
		];
	}	public function authorize()
	{		// 只允许登陆用户
		// 返回 \Auth::check();
		// 允许所有用户登入
		return true;
	}	// 可选: 重写基类方法
	public function forbiddenResponse()
	{		// 这个是可选的, 当认证失败时返回自定义的 HTTP 响应. 
		// (框架默认的行为是带着错误信息返回到起始页面)
		// 可以返回 Response 实例, 视图, 重定向或其它信息
		return Response::make('Permission denied foo!', 403);
	}	// 可选: 重写基类方法
	public function response()
	{		// 如果需要自定义在验证失败时的行为, 可以重写这个方法
		// 了解有关基类中这个方法的默认行为,可以查看:
		// https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Http/FormRequest.php
	}
}

接下来, 用 php artisan serve 或者你自己喜欢的方式启动服务器. 提交表单, 你可以看到我们并没有往控制器中添加任何一行验证逻辑, 但是验证规则已经生效了.

其它用例

如果对 "新增" 和 "编辑" 有不同的规则, 或者根据不同的输入进行不同的验证, 要怎么办呢? 这里有几个可以参考的例子, 虽然还不能确定这些就是 "最佳实践":

采用分开的 form requests

Laravel 并没有规定你不能对 "新增" 和 "编辑" 操作采用不同的 form request 类. 所以你可以创建一个包含所有规则的 FriendFormRequest 作为基类, 然后把它扩展为 addFriendFormRequest 和 editFriendFormRequest 两个子类, 每个子类都可以实现各自的默认行为.

采用条件判断逻辑

rules() 作为一个方法而不是属性, 带来的好处就是你可以在方法中添加判断逻辑:

...class UserFormRequest extends FormRequest{
	...	protected $rules = [	
		'email_address' => 'required',		'password' => 'required|min:8',
	];	public function rules()
	{
		$rules = $this->rules;		// 根据不同的情况, 添加不同的验证规则
		if ($someTestVariableShowingThisIsLoginInsteadOfSignup)
		{
			$rules['password'] = 'min:8';
		}		return $rules;
	}
}

也可以在 authorize 方法中添加逻辑, 比如:

...class FriendFormRequest extends FormRequest{
	...	public function authorize()
	{		if ( ! Auth::check() )
		{			return false;
		}
		$thingBeingEdited = Thing::find(Input::get('thingId'));		// 如果是编辑操作, 或者当前用户不是对象创建者
		if ( ! $thingBeingEdited || $thingBeingEdited->owner != Auth::id()) {			return false;
		}		return true;
	}
}

自定义校验

除了上面的方式, 如果需要对验证逻辑进行更深入的控制, 可以重写提供校验对象实例的方法. 下面是一个简单的实例, 后续会专门写一篇文章来解释:

...class FriendFormRequest extends FormRequest{	public function validator(ValidationService $service)
	{
		$validator = $service->getValidator($this->input());		// 可选: 通过新的 ->after() 方法来进行自定义
		$validator->after(function() use ($validator)) {			// 在这里可以做更多更深入的校验
			$validator->errors()->add('field', 'new error);
		}
	}
}

ValidatesWhenResolved 接口

后续还会有一篇有关 ValidatesWhenResolved 接口的文章, 不过那篇文章重点讨论的是对方法/路由等的校验. IOC 何时提供什么东西, 这个在 Laravel 5.0 版已经分离出一个单独的接口. 官方文档: https://github.com/illuminate/contracts/blob/master/Validation/ValidatesWhenResolved.php

其它可自定义的参数:

$redirect : 校验失败时要重定向到的 URI. $redirectRoute : 校验失败时要重定向到的路由.$redirectAction : 校验失败时要重定向到的方法. $dontFlash : 重定向时不要传递的输入项的键 (默认值: ['password', 'password_confirmation']).

写在最后

通过文本可以看到, Form Requests 对于简化表单请求的数据校验是非常强大和方便的. 如果你阅读本文觉得还不够, 可以观看关于 Form Request 的 这个视频 .

本文写作时, Laravel 5.0 还未正式发布, 因此上述内容最终可能还会有修改, 或者作者遗漏了某些东西. 如果你有建议或者对文章内容的修正, 可以在 给译者发邮件 或者(在 Twitter 上直接联系原作者)

[http://twitter.com/stauffermatt].