我发现了Request::replace方法,它允许替换Request中的输入参数。
但是目前我只能看到一种实现它的方法-在每个控制器动作中编写相同的替换输入代码。
是否有可能以某种方式分组代码,这将在请求成功验证后执行,但在控制器动作开始之前?
例如,我需要在我的api中支持ISO2语言,但在引擎盖下,我必须将它们转换为真正存储在数据库中的遗留语言。目前我有这个代码在控制器:
// Controller action context
$iso = $request->input('language');
$legacy = Language::iso2ToLegacy($iso);
$request->replace(['language' => $legacy]);
// Controller action code starts
我认为你正在寻找的是ValidatesWhenResolvedTrait
特征中的passedValidation()
方法
如何使用:
- 创建自定义请求:
php artisan make:request UpdateLanguageRequest
- 将验证规则放入
UpdateLanguageRequest
类中的rules()
方法 - 使用
passedValidation()
方法在成功验证后对请求对象进行任何操作
namespace App'Http'Requests;
use App'...'Language;
class UpdateLanguageRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
// here goes your rules, f.e.:
'language' => ['max:255']
];
}
protected function passedValidation()
{
$this->replace(['language' => Language::iso2ToLegacy($this->language)]);
}
}
- 用
UpdateLanguageRequest
类代替Request
public function someControllerMethod(UpdateLanguageRequest $request){
// the $request->language data was already modified at this point
}
*也许你想使用merge
而不是replace
方法,因为replace将替换请求中的所有其他数据,merge
方法将仅替换特定值
根据Alexander Ivashchenko上面的答案,这个解决方案对我有效:
<?php
namespace App'Http'Requests'User;
class UserUpdateRequest extends UserRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name'=>'required|string',
'email'=>'required|string|email',
'password'=>'min:8'
];
}
}
父UserRequest
类:
<?php
namespace App'Http'Requests'User;
use Illuminate'Foundation'Http'FormRequest;
use Illuminate'Support'Facades'Hash;
abstract class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Handle a passed validation attempt.
*
* @return void
*/
protected function passedValidation()
{
if ($this->has('password')) {
$this->merge(
['password' => Hash::make($this->input('password'))]
);
}
}
public function validated(): array
{
if ($this->has('password')) {
return array_merge(parent::validated(), ['password' => $this->input('password')]);
}
return parent::validated();
}
}
我也重写了有效的方法。如果我们单独访问每个输入元素,他的答案是有效的,但是为了在下面的控制器中使用批量分配,我们需要重写validated
。
...
public function update(UserUpdateRequest $request, User $user): JsonResource
{
$user->update($request->validated());
...
}
...
这是因为validated
方法直接从Validator而不是Request获取数据。另一个可能的解决方案是使用DTO方法的自定义验证器,但对于上面的简单内容来说,这已经足够了。
是否有可能以某种方式将代码分组,将在之后执行请求验证成功,但在控制器动作之前开始了吗?
您可以使用中间件作为验证器,例如:
namespace App'Http'Middleware;
use Closure;
use Illuminate'Http'JsonResponse;
class InputValidator
{
public function handle($request, Closure $next, $fullyQualifiedNameOfModel)
{
$model = app($fullyQualifiedNameOfModel);
$validator = app('validator')->make($request->input(), $model->rules($request));
if ($validator->fails()) {
return $this->response($request, $validator->errors());
}
return $next($request);
}
protected function response($request, $errors)
{
if($request->ajax()) {
return new JsonResponse($errors, 422);
}
return redirect()->back()->withErrors($errors)->withInput();
}
}
在App'Http'Kernel.php
类的$routeMiddleware
末尾添加以下条目:
'validator' => 'App'Http'Middleware'InputValidator'
在Eloquent Model
中添加rules
方法,例如,app'Product.php
是模型,rules
方法声明如下:
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules('Illuminate'Http'Request $request)
{
return [
'title' => 'required|unique:products,title,'.$request->route()->parameter('id'),
'slug' => 'required|unique:products,slug,'.$request->route()->parameter('id'),
];
}
这样声明路由:
$router->get('create', [
'uses' => 'ProductController@create',
'as' => 'Product.create',
'permission' => 'manage_tag',
'middleware' => 'validator:App'Product' // Fully qualified model name
]);
您可以使用数组添加更多的middleware
,例如:
'middleware' => ['auth', 'validator:App'Product']
这是一种使用单个middleware
替换FormRequest
的方法。我使用这个middleware
和model name
作为参数,使用单个middleware
而不是每个控制器的单个FormRequest
类来验证我的所有模型。
这里,validator
是middleware
, App'Product
是模型名,我将其作为参数传递,并从middleware
中验证该模型。
根据你的问题,控制器内部的代码将只在输入验证通过后执行,否则redirect/ajax response
将完成。出于特定的原因,您可以创建一个特定的middleware
。这只是一个可以在您的情况下使用的想法IMO,我的意思是您可以添加代码来替换验证通过后特定middleware
中的输入。
使用merge代替replace
$iso = $request->merge('language');
$legacy = Language::iso2ToLegacy($iso);
$request->merge(['language' => $legacy]);