【问题标题】:Laravel 5 Form Request data pre-manipulationLaravel 5 表单请求数据预操作
【发布时间】:2015-05-05 10:46:31
【问题描述】:

我正在处理一个用户可以更新其出生日期的表单。该表单为用户提供了daymonthyear 的3 个单独字段。在服务器端,我当然想将这 3 个单独的字段视为一个值,即yyyy-mm-dd

所以在验证和更新我的数据库之前,我想通过将yearmonthday- 字符连接来更改表单请求以创建一个date_of_birth 字段,以创建我需要的日期格式(并且可能取消设置原来的 3 个字段)。

使用我的控制器手动实现这一点不是问题。我可以简单地获取输入,将由- 字符分隔的字段连接在一起并取消设置它们。然后我可以在传递给处理处理的命令之前手动验证。

但是,我更喜欢使用 FormRequest 来处理验证并将其注入到我的控制器方法中。因此,我需要一种在执行验证之前实际修改表单请求的方法。

我确实发现了以下类似的问题:Laravel 5 Request - altering data

它建议覆盖表单请求上的all 方法,以包含在验证之前操作数据的逻辑。

<?php namespace App\Http\Requests;

class UpdateSettingsRequest extends Request {

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [];
    }

    public function all()
    {
        $data = parent::all();
        $data['date_of_birth'] = 'test';
        return $data;
    }

这一切都很好,有利于验证,但是覆盖all 方法实际上并没有修改表单请求对象上的数据。因此,在执行命令时,表单请求包含原始未修改的数据。除非我使用现在被覆盖的all 方法来提取数据。

我正在寻找一种更具体的方法来修改我的表单请求中的数据,而不需要调用特定的方法。

干杯

【问题讨论】:

    标签: php validation laravel laravel-5


    【解决方案1】:

    您仍然会覆盖 all() 方法 - 但可以这样尝试

    public function all()
    {
        $input = $this->all();
        $input['date_of_birth'] = $input['year'].'-'.$input['month'].'-'.$input['day'];
        $this->replace($input);
        return $this->all();
    }
    

    那么你实际上并没有自己调用该方法——它会在执行规则时由验证器本身调用。

    【讨论】:

    • 感谢您的回复。没有更清洁的“一次性”方式吗?感觉很脏,每次请求数据时都必须对数据进行操作。从代码来看,似乎每次请求数据时都会调用all() 函数(即来自inputexcept 等)。
    • 这是一种非常干净的方式——因为它在 FormRequest 对象中被巧妙地处理。这意味着控制器和应用程序的其他方面不知道发生了什么。每次调用all() 时都会发生这种情况,这不是问题——您所说的优化级别是微不足道的。专注于编写好的干净可维护的代码会有更好的结果:)
    • 是的,在这种情况下这绝对是微不足道的。但从理论上讲,如果这里有很多事情要做,那可能就是另一回事了。我更喜欢尽可能编写最佳代码。我将这种方法比作获取我们在每次迭代中循环的数组的长度。它有效,优化可能是微不足道的,但它都安装了。我尝试寻找其他方法来覆盖并得出结论,在Symfony\Component\HttpFoundation\Request 中找到的initialize 方法可能是要走的路。对此有什么想法吗?它是从构造函数中调用的。
    • 问题是 - all() 方法已经每次迭代无论如何上进行数组循环。如果您查看原始的 all() 方法 - 就是这样:return array_replace_recursive($this-&gt;input(), $this-&gt;files-&gt;all())。因此,您进行此修改不会对您的性能产​​生任何影响。您正在尝试优化不需要优化的流程...
    【解决方案2】:

    注意:这个问题和答案都是在 Laravel 5.1 发布之前发布的,并且都是针对 5.0 的。对于 5.1 及更高版本,请参阅 @Julia Shestakova 的 this answer 或 @BenSampo 的 this answer 以获得更现代的解决方案。


    经过一番折腾,我想出了以下几点:

    app/Http/Requests/Request.php

    <?php namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    abstract class Request extends FormRequest {
    
        /**
        * Override the initialize method called from the constructor to give subclasses
        * an opportunity to modify the input before anything happens.
        *
        * @param array $query
        * @param array $request
        * @param array $attributes
        * @param array $cookies
        * @param array $files
        * @param array $server
        * @param null $content
        */
        public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
        {
            parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
    
            // Grab the input
            $data = $this->getInputSource()->all();
            // Pass it off to modifyInput function
            $data = $this->modifyInput($data);
            // Replace modified data back into input.
            $this->getInputSource()->replace($data);
        }
    
    
        /**
        * Function that can be overridden to manipulate the input data before anything
        * happens with it.
        *
        * @param array $data The original data.
        * @return array The new modified data.
        */
        public function modifyInput(array $data)
        {
            return $data;
        }
    
    }
    

    然后在扩展类时,您可以像这样覆盖modifyInput 方法:

    app/Http/Requests/TestRequest.php

    <?php namespace App\Http\Requests;
    
    class TestRequest extends Request {
    
        public function authorize()
        {
            return true;
        }
    
        public function rules()
        {
            return [];
        }
    
        /**
         * Modify the input.
         */
        public function modifyInput(array $data)
        {
            $data['date_of_birth'] = 'something';
    
            // Make sure to return it.
            return $data;
        }
    
    }
    

    这似乎可以满足我的需求。我不确定这样做有什么缺点,所以我欢迎任何 cmets/批评。

    上面 The Shift Exchange 给出的答案也可以正常工作。

    【讨论】:

    • +1 这实际上是一种非常好的方式,我喜欢它。虽然我会通过 $source = $this-&gt;getInputSource(); 缓存 sourceInput
    【解决方案3】:

    我也需要一种快速而肮脏的方法来实现这一点。我想使用 The Shift Exchanges 解决方案,但由于对 $this 的调用创建了无限递归循环,因此它不起作用。快速更改以引用父方法将解决此问题:

    public function all()
    {
        $input = parent::all();
        $input['date_of_birth'] = $input['year'].'-'.$input['month'].'-'.$input['day'];
        $this->replace($input);
        return parent::all();
    }
    

    还有其他需要帮助的人。

    【讨论】:

      【解决方案4】:

      在 laravel 5.1 中你可以做到这一点

      <?php namespace App\Http\Requests;
      
      class UpdateSettingsRequest extends Request {
      
      public function authorize()
      {
          return true;
      }
      
      public function rules()
      {
          return [];
      }
      
      protected function getValidatorInstance()
      {
          $data = $this->all();
          $data['date_of_birth'] = 'test';
          $this->getInputSource()->replace($data);
      
          /*modify data before send to validator*/
      
          return parent::getValidatorInstance();
      }
      

      【讨论】:

      • 虽然在我问这个问题的时候(2015 年 3 月),Laravel 5.1 还没有发布(它是在 2015 年 6 月发布的),但我会接受这个答案。对于 Laravel 5.0,请参阅其他一些答案。谢谢,朱莉娅!
      【解决方案5】:

      我对 Julia Logvina 采用了类似的方法,但我认为这种方法是在验证之前添加/修改字段的一种更优雅的方法(Laravel 5.1)

      <?php
      
      namespace App\Http\Requests;
      
      use App\Http\Requests\Request;
      
      class UpdateSettingsRequest extends Request
      {
          /**
           * 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 [];
          }
      
      
          /** 
           * Extend the default getValidatorInstance method
           * so fields can be modified or added before validation
           *
           * @return \Illuminate\Contracts\Validation\Validator
           */
          protected function getValidatorInstance()
          {
      
              // Add new data field before it gets sent to the validator
              $this->merge(array('date_of_birth' => 'test'));
      
              // OR: Replace ALL data fields before they're sent to the validator
              // $this->replace(array('date_of_birth' => 'test'));
      
              // Fire the parent getValidatorInstance method
              return parent::getValidatorInstance();
      
          }
      
      }
      

      这将扩展默认的getValidatorInstance,因此我们可以在请求到达验证器之前修改请求中的输入值(防止它使用原始未修改的数据)。修改数据后,它会触发原始的getValidatorInstance,然后一切都会照常进行。

      您可以在请求中使用$this-&gt;replace(array())$this-&gt;merge(array()) 新字段。我在上面的 sn-p 中包含了一个如何同时执行这两个操作的示例。

      replace() 会将所有字段替换为您提供的数组。

      merge() 将在您的请求中添加一个新字段。

      【讨论】:

      • 有没有办法改变 FormRequest 重定向到的位置,还是只在出现错误时才重定向回来?
      【解决方案6】:

      我认为这是最好的方法:Laravel 5.1 Modify input before form request validation

      在 Laravel 5.4+ 中,有一个专门的方法来处理这样的事情,所以请使用它:prepareForValidation

      【讨论】:

      • 当然,在 5.4 中,这无疑是该问题的最佳解决方案。但是,对于 5.4 之前的版本(这个问题是在 2015 年 3 月提出的,当时的版本是 5.0),那么下面给出的其他答案更合适,例如 @Julia Logvina 为 5.1 给出的答案,这是一个长期支持 (LTS) 版本
      • Jep,但是像我这样的人如果他们在 5.x 上寻找一般主题仍然会发现这个,所以我认为提及 5.4+ 的解决方案是有效的;)
      • 感谢@Thomas Venturini!使用 5.5 肯定有帮助。 +1
      【解决方案7】:

      从 Laravel 5.4 开始,您可以在 FormRequest 类上使用 prepareForValidation 方法。

      <?php
      
      namespace App\Http\Requests;
      
      use Illuminate\Foundation\Http\FormRequest;
      
      class StorePostRequest extends FormRequest
      {
          /**
           * Get the validation rules that apply to the request.
           *
           * @return array
           */
          public function rules()
          {
              return [
                  'title' => 'required|max:200',
                  'body' => 'required',
                  'tags' => 'required|array|max:10',
                  'is_published' => 'required|boolean',
                  'author_name' => 'required',
              ];
          }
      
          /**
           * Prepare the data for validation.
           *
           * @return void
           */
          protected function prepareForValidation()
          {
              $this->merge([
                  'title' => fix_typos($this->title),
                  'body' => filter_malicious_content($this->body),
                  'tags' => convert_comma_separated_values_to_array($this->tags),
                  'is_published' => (bool) $this->is_published,
              ]);
          }
      }
      

      这里有更详细的描述: https://sampo.co.uk/blog/manipulating-request-data-before-performing-validation-in-laravel

      【讨论】:

      • 有没有办法改变 FormRequest 重定向到的位置,还是只在出现错误时才重定向回来?
      • 为什么直到版本 6.x 才记录此方法? :-/
      猜你喜欢
      • 1970-01-01
      • 2015-04-20
      • 2015-10-05
      • 2016-05-20
      • 2015-07-15
      • 2017-05-20
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      相关资源
      最近更新 更多