Laravel 请求在验证失败时不返回修改后的请求

Posted

技术标签:

【中文标题】Laravel 请求在验证失败时不返回修改后的请求【英文标题】:Laravel Request does not return the modified request on validation fail 【发布时间】:2021-03-24 16:15:21 【问题描述】:

我最近更新了我的控制器以在保存之前使用请求来验证数据,最初我在控制器路由中使用了$request->validate(),但我现在正处于我真的需要将其分离到请求中的阶段。

问题

在进行验证之前,我需要更改请求中的一些参数,我发现这可以使用prepareForValidation() 方法完成,而且效果很好,在验证期间请求中的值已被更改。如果验证失败,我的问题就会出现。我需要能够将我已更改的请求返回到视图,目前,在重定向之后,它似乎正在使用该请求,就像我运行 prepareForValidation() 之前一样。 (即,将标题返回为 'ABCDEFG' 而不是 'Changed The Title')。

在阅读了其他 SO 帖子和 Laravel 论坛帖子之后,看起来我需要覆盖 FormRequest::failedValidation() 方法(我已经完成了,请参见下面的代码),但是我仍在努力寻找方法将我更改后的请求传回。我已尝试编辑 failedValidation() 方法,我已在下方提供了详细信息。

期望与现实

期待

    用户输入“ABCDEFG”作为标题并按保存。 请求中的标题(使用prepareForValidation())被更改为“更改标题”。 验证失败,用户被重定向回创建页面。 标题字段的内容现在是“更改标题”。

现实

    用户输入“ABCDEFG”作为标题并按保存。 请求中的标题(使用prepareForValidation())被更改为“更改标题”。 验证失败,用户被重定向回创建页面。 标题字段的内容显示为“ABCDEFG”。

我尝试过的

将请求传递给ValidationException 类。

深入研究代码后,看起来ValidationException 允许将响应作为参数传递。

    protected function failedValidation(Validator $validator)
    
        throw (new ValidationException($validator, $this))
            ->errorBag($this->errorBag)
            ->redirectTo($this->getRedirectUrl());
    

但是这会导致Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::addCookieToResponse 中的错误Call to undefined method Symfony\Component\HttpFoundation\HeaderBag::setCookie()

将请求闪烁到会话

我的下一次尝试是将我的请求闪现到会话中,这似乎不起作用,而不是我修改后的请求在会话中,它看起来是我运行 prepareForValidation() 之前的请求。

    protected function failedValidation(Validator $validator)
    
        $this->flash();

        throw (new ValidationException($validator))
            ->errorBag($this->errorBag)
            ->redirectTo($this->getRedirectUrl());
    

返回响应而不是异常

我最后一次尝试使用withInput()而不是异常返回响应。

    protected function failedValidation(Validator $validator)
    
        return redirect($this->getRedirectUrl())
        ->withErrors($validator)
        ->withInput();
    

但是看起来代码继续进入BlogPostController::store() 方法,而不是重定向回视图。

此时我没有想法,如果验证失败,我似乎无法将更改后的请求返回到视图!

其他说明

    我几乎是 Laravel 新手,我曾使用过松散地基于 Laravel 的自定义框架,但这是我的第一个 CMS 项目。 我完全理解我可能走错了路,也许有更好的方法来更改请求并在验证失败时将其传回? 我这样做的目的是什么?最主要的是活动复选框。默认情况下它被选中(参见下面的刀片),如果用户取消选中它并按下保存,active 不会在 HTTP 请求中传递,因此 active 在 Laravel 请求对象中不存在并且当用户返回,激活的复选框在不应该被选中时再次被选中。 那你为什么在你的例子中使用title?我在帖子中使用了title,因为我认为更容易看到我想要实现的目标。

感谢任何帮助,因为我目前已经花费了好几个小时试图解决这个问题。 ????

相关代码

BlogPostController.php

<?php

namespace App\Http\Controllers;

use App\BlogPost;
use Illuminate\Http\Request;
use App\Http\Requests\StoreBlogPost;

class BlogPostController extends Controller


    /**
     * Run the auth middleware to make sure the user is authorised.
     */
    public function __construct()
    
        $this->middleware('auth');
    

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    
        return view('admin.blog-posts.create');
    

    /**
     * Store a newly created resource in storage.
     *
     * @param  StoreBlogPost    $request
     * @return \Illuminate\Http\Response
     */
    public function store(StoreBlogPost $request)
    
        $blogPost = BlogPost::create($request->all());

        // Deal with the listing image upload if we have one.
        foreach ($request->input('listing_image', []) as $file) 
            $blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('listing_image');
        

        // Deal with the main image upload if we have one.
        foreach ($request->input('main_image', []) as $file) 
            $blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('main_image');
        

        return redirect()->route('blog-posts.edit', $blogPost->id)
            ->with('success', 'The blog post was successfully created.');
    


// Removed unrelated controller methods.

StoreBlogPost.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;

class StoreBlogPost extends FormRequest

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    
        return true;
    

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    
        return [
            'title'         => 'required',
            'url'           => 'required',
            'description'   => 'required',
            'content'       => 'required',
        ];
    

    /**
     * Get the error messages for the defined validation rules.
     *
     * @return array
     */
    public function messages()
    
        return [
            'title.required'        => 'The Title is required',
            'url.required'          => 'The URL is required',
            'description.required'  => 'The Description is required',
            'content.required'      => 'The Content is required',
        ];
    

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function prepareForValidation()
    
        $this->merge([
            'active' => $this->active ?? 0,
            'title' => 'Changed The Title',
        ]);
    

    /**
     * @see FormRequest
     */
    protected function failedValidation(Validator $validator)
    
        throw (new ValidationException($validator))
            ->errorBag($this->errorBag)
            ->redirectTo($this->getRedirectUrl());
    

create.blade.php

@extends('admin.layouts.app')

@section('content')
<div class="edit">
    <form action=" route('blog-posts.store') " method="POST" enctype="multipart/form-data">
        @method('POST')
        @csrf

        <div class="container-fluid">
            <div class="row menu-bar">
                <div class="col">
                    <h1>Create a new Blog Post</h1>
                </div>
                <div class="col text-right">
                    <div class="btn-group" role="group" aria-label="Basic example">
                        <a href=" route('blog-posts.index') " class="btn btn-return">
                            <i class="fas fa-fw fa-chevron-left"></i>
                            Back
                        </a>

                        <button type="submit" class="btn btn-save">
                            <i class="fas fa-fw fa-save"></i>
                            Save
                        </button>
                    </div>
                </div>
            </div>
        </div>

        <div class="container-fluid">
            <div class="form-group row">
                <label for="content" class="col-12 col-xl-2 text-xl-right col-form-label">Active</label>
                <div class="col-12 col-xl-10">
                    <div class="custom-control custom-switch active-switch">
                      <input type="checkbox" name="active" value="1" id="active" class="custom-control-input"  old('active', '1') ? 'checked' : '' >
                      <label class="custom-control-label" for="active"></label>
                    </div>
                </div>
            </div>

            <div class="form-group row">
                <label for="title" class="col-12 col-xl-2 text-xl-right col-form-label required">Title</label>
                <div class="col-12 col-xl-10">
                    <input type="text" name="title" id="title" class="form-control" value=" old('title', '') ">
                </div>
            </div>
        </div>
    </form>
</div>
@endsection

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,这是我在挖掘 laravel 代码后找到的解决方案。

Laravel 似乎为FormRequest 创建了一个不同的对象,所以你可以这样做。

protected function failedValidation(Validator $validator)

    // Merge the modified inputs to the global request.
    request()->merge($this->input());

    parent::failedValidation($validator);

【讨论】:

以上是关于Laravel 请求在验证失败时不返回修改后的请求的主要内容,如果未能解决你的问题,请参考以下文章

laravel 5.4在请求中验证之前修改数据[关闭]

在 laravel 5 中的表单请求验证后传递旧输入

Laravel 5表单请求验证返回禁止错误

使用 laravel 请求类时,在数组错误上调用成员函数失败()

Laravel 作业 - 作业失败后从 failed() 方法访问修改后的属性值

laravel中StartSession中间件的问题