[译]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.php
Route::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.php

namespace 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 上直接联系原作者.

               

[译]Laravel 5.0 之 表单验证类 (Form Requests)》上有4条评论

  1. 培训请求

    可否联系做一些lavarel5培训? qq:110291378

  2. 慕薇

    有一个问题,不清楚怎么验证单条数据并使用Flash包给出具体是哪条验证?我现在是直接写在Controller里面的.

    1. 小李刀刀 文章作者

      FormRequest 里的 rules() 里面的每一条规则不就是针对单条数据的?你是希望 AJAX 方式验证在填写每一个表单项并失去焦点后单独验证吗?那是需要单独写才行。

    2. 小李刀刀 文章作者

      如果你是想访问 MessageBag 中指定的单条数据的验证情况,可以使用:
      $errors->get($key)
      $errors->first($key)
      $errors->has($key)
      这些方法。更多的方法可以查看 IlluminateSupportMessageBag 的源码。

评论已关闭。