Laravel 5.5 - 同时验证多个表单请求

Posted

技术标签:

【中文标题】Laravel 5.5 - 同时验证多个表单请求【英文标题】:Laravel 5.5 - Validate Multiple Form Request - at the same time 【发布时间】:2018-06-30 10:51:31 【问题描述】:

这个问题已经被问到here 之前版本的 laravel 并且还没有回答。

我有一个html form,它使用三个不同的Form Request Validations 进行验证。我能够做到这一点。但是,问题是,表单验证是一一进行的。不是同时。

如果第一个表单请求引发验证错误,则表单将返回到view,因此不会评估其余两个表单,因此无法向用户显示正确的验证错误。

我想要的是:同时用三个表单验证请求rules验证表单

控制器:

public function store(TransportationRequest $request1, PurchaseRequest $request2, SaleRequest $request3)
    
        //do actions here
    

我尝试过一一继承表单请求,但未能成功。

编辑:

更具体地回答我的问题:

我确实为purchasetransporataionsale 设置了三个单独的表格,它们分别使用PurchaseRequestTransportationRequestSaleRequest 进行单独评估。

但有一个特殊情况,单个表单处理purchasetransporataionsale我想结合使用三个表单请求规则来验证表单,因为我不想再次编写相同的验证规则。

这个

注意:单独表格和合并表格中的字段相同。

谢谢..

【问题讨论】:

为什么需要 3 个单独的请求?请求背后的想法是它映射到一个传入的 HTTP 请求,其中只有一个。话虽如此,您可以(我认为 - 自己没有尝试过)创建另一个请求对象,它接受您的 3 个请求作为其构造函数的参数。 Laravel 的 IoC 容器应该能够将每个请求项传递给构造函数,然后您可以使用每个请求实例来构建一组规则和消息等。 我的问题是您如何设法同时发布 3 个表单,或者只是将单个表单分成 3 个部分的不同请求?原始问题的答案是您需要手动制作校准器并在每个校准器上运行passes() 方法,然后按照here 的描述收集错误 您能否举例说明何时需要单独的公司请求类?您链接的问题对多个请求参数没有意义。我从来没有遇到过一个坚定的要求是不够的。 @jonathon,我也尝试过这种方式,但在将第一个请求传递给构造函数后它也会返回查看。我不熟悉 Laravel 的 IoC 容器,也无法在 docs(5.5) 中找到它。 @apokryfos 问题已更新。我想将验证逻辑分开以形成请求,这就是我不使用手动验证器的原因 【参考方案1】:

我将创建包含每个 FormRequest 规则的特征 - 购买、运输和销售。在其特定的 FormRequest 中使用特征,然后当您需要所有规则时,您可以在组合的 FormRequest 中使用所有三个特征,然后合并规则数组。

【讨论】:

rules() 方法的多个特征在同一个表单请求类中是used 时,您将有不明确的方法声明。【参考方案2】:

当验证失败时,FormRequest 会引发 Illuminate\Validation\ValidationException 异常,该异常具有 redirectTo method,并从那里引发 Exception Handler performs the redirect。

你可以通过在你的控制器中手动运行表单请求来实现你想要的行为,在 try/catch 块中捕获错误并在重定向之前组合错误包,或者如果你必须通过 Laravel 将它们注入到你的控制器,那么您需要添加自己的异常处理程序来捕获所有错误,将它们组合起来,然后在最终表单请求运行后重定向。

但是,值得注意的是,这两种方法都不是很好:它们很麻烦,并且会给您带来比它们解决的问题更多的问题。如果你想编写一个可维护的应用程序,你应该尽可能地坚持 Laravel 的做事方式。

存在表单请求来验证表单,因此,每个表单应该有一个表单请求,如果您希望根据不同的规则集编写表单请求,那么应该在表单请求中完成,例如:

    为您的表单定义您的表单请求php artisan make:request StoreMultipleForm

    StoreMultipleForm 上的rules 方法获取其他每个表单请求的rules,然后将它们一起返回,例如:

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    
        $formRequests = [
          TransportationRequest::class,
          PurchaseRequest::class,
          SaleRequest::class
        ];
    
        $rules = [];
    
        foreach ($formRequests as $source) 
          $rules = array_merge(
            $rules,
            (new $source)->rules()
          );
        
    
        return $rules;
    
    

    在您的控制器中使用新组合的表单请求,例如:

    public function store(StoreMultipleForm $request)
    
        // Do actions here.
    
    

这种方法的优点是它是独立的,它符合一个表单请求的期望,它不需要更改您正在组合的表单请求,如果您需要添加此表单独有的其他规则,您可以这样做而无需创建另一个表单请求。

【讨论】:

您写了一个很好的答案,但为了保持完整,我认为您需要在每个答案上调用授权函数。【参考方案3】:

如果我理解正确,您有 3 个表单,每个表单都有自己的表单请求来处理各自的验证。您还有另一个表单,它在其他地方组合了这 3 个表单,您不想通过重写这些验证规则来重复自己。

在这种情况下,我仍然建议使用单个表单请求,但尝试结合每个单独请求的规则。例如,您使用静态方法为 3 个单独的表单请求定义规则,并让每个单独的请求调用自己的静态方法来获取它们:

class TransportationRequest extends FormRequest

    public static function getRules()
    
        return []; // Return rules for this request
    

    public function rules()
    
        return static::getRules();
    


class PurchaseRequest extends FormRequest

    public static function getRules()
    
        return []; // Return rules for this request
    

    public function rules()
    
        return static::getRules();
    


class SaleRequest extends FormRequest

    public static function getRules()
    
        return []; // Return rules for this request
    

    public function rules()
    
        return static::getRules();
    

然后让您的合并请求合并所有三个集合:

class CombinedRequest extends FormRequest

    public function rules()
    
        return array_merge(
            TransportationRequest::getRules(),
            SaleRequest::getRules(),
            PurchaseRequest::getRules()
        );
    

然后您可以在控制器方法中使用单个 CombinedRequest。当然,如果您不喜欢静态方法方法,在您的组合请求 rules 方法中,您可以将每个单独的请求 new 并在每个请求上调用 rules 方法并合并结果。

class CombinedRequest extends FormRequest

    public function rules()
    
        $transportation = (new TransportationRequest())->rules();
        $sale = (new SaleRequest())->rules();
        $purchase = (new PurchaseRequest())->rules();

        return array_merge(
            $transportation,
            $sales,
            $purchase
        );
    

【讨论】:

【参考方案4】:

您可以连接所有规则并手动验证:

$allRules = (new TransportationRequest)->rules() + (new PurchaseRequest)->rules() + (new SaleRequest)->rules();
Validator::make($request->all(), $allRules)->validate();

【讨论】:

【参考方案5】:

我知道这是一个很老的问题,但是我因为无法将表单请求链接在一起而感到恼火,所以我为此制作了一个作曲家包,所以你不必这样做。

https://github.com/sharpie89/laravel-multiform-request

【讨论】:

【参考方案6】:

我最近遇到了这个问题,并这样解决了:

public function rules()

    $request1 = RequestOne::createFrom($this);
    $request2 = RequestTwo::createFrom($this);

    return array_merge(
        $request1->rules(),
        $request2->rules()
    );

【讨论】:

【参考方案7】:

事实证明,如果您解决了请求,验证就会启动。 需要注意的一点是,所有验证对象不会立即启动,但根据您的用例,这可能会更简单。

    public function store()
    
        // request()->replace([ some fields to be validated ]);
        resolve(TransportationRequest::class);
        resolve(PurchaseRequest::class);
        resolve(SaleRequest::class);
        // request is valid
    

【讨论】:

它应该是一个可接受的答案,但可以通过在每个 resolve 和 @ 上添加 try.. catch(Illuminate\Validation\ValidationException $ex)... 来改进答案987654323@ 应替换为 app :)【参考方案8】:

可以一个一个手动执行Request类:

public function store()

    $isValid = true;
    try
        app(TransportationRequest::class);
     catch (Illuminate\Validation\ValidationException $ex)
        $isValid = false ;
    
    try
        app(PurchaseRequest::class);
     catch (Illuminate\Validation\ValidationException $ex)
        $isValid = false ;
    
    try
        app(SaleRequest::class);
     catch (Illuminate\Validation\ValidationException $ex)
        $isValid = false ;
    
    if (!$isValid)
        throw $ex;
    

如果其中一个验证失败,用户将被重定向回上一页并出现错误。

【讨论】:

以上是关于Laravel 5.5 - 同时验证多个表单请求的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.5 无法在表单请求类中制作验证器

Laravel 5.5 / 验证器 / 自定义规则

laravel 5.5 修改注册表单验证规则

表单请求验证中的 Laravel“独特”自定义消息验证

Laravel 5.5 的自定义验证对象/类

如何在 Laravel 5.5 中为选定的请求类设置自定义响应