Laravel 5 表单请求数据预操作
Posted
技术标签:
【中文标题】Laravel 5 表单请求数据预操作【英文标题】:Laravel 5 Form Request data pre-manipulation 【发布时间】:2015-05-05 10:46:31 【问题描述】:我正在处理一个用户可以更新其出生日期的表单。该表单为用户提供了day
、month
和year
的3 个单独字段。在服务器端,我当然想将这 3 个单独的字段视为一个值,即yyyy-mm-dd
。
所以在验证和更新我的数据库之前,我想通过将year
、month
和day
与-
字符连接来更改表单请求以创建一个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
方法来提取数据。
我正在寻找一种更具体的方法来修改我的表单请求中的数据,而不需要调用特定的方法。
干杯
【问题讨论】:
【参考方案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()
函数(即来自input
、except
等)。
这是一种非常干净的方式——因为它在 FormRequest 对象中被巧妙地处理。这意味着控制器和应用程序的其他方面不知道发生了什么。每次调用all()
时都会发生这种情况,这不是问题——您所说的优化级别是微不足道的。专注于编写好的干净可维护的代码会有更好的结果:)
是的,在这种情况下这绝对是微不足道的。但从理论上讲,如果这里有很多事情要做,那可能就是另一回事了。我更喜欢尽可能编写最佳代码。我将这种方法比作获取我们在每次迭代中循环的数组的长度。它有效,优化可能是微不足道的,但它都安装了。我尝试寻找其他方法来覆盖并得出结论,在Symfony\Component\HttpFoundation\Request
中找到的initialize
方法可能是要走的路。对此有什么想法吗?它是从构造函数中调用的。
问题是 - all()
方法已经在每次迭代无论如何上进行数组循环。如果您查看原始的 all()
方法 - 就是这样:return array_replace_recursive($this->input(), $this->files->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->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->replace(array())
或$this->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 才记录此方法? :-/以上是关于Laravel 5 表单请求数据预操作的主要内容,如果未能解决你的问题,请参考以下文章