Laravel 中多态关系的表单验证
相信大家使用 Laravel 开发应用的时候都会有评论模块吧,而且我们通常将该模块设计为多态关系(如果你对这个关系还不明白的话,请赶紧打开 Laravel 文档数据库关系章节复习一遍吧!)。
先看看我们的数据库结构:
id
commentable_id
commentable_type
body
那么遇到一个问题,如果写入数据呢?一般来讲有两种方式,而我们通常用的一种是从父模型使用关系写入,比如我们有一个 App\Thread
类,它里面对评论的关系是这样的:
class Thread
{
public function comments() {
$this->morphToMany(Comment::class, 'commentable');
}
}
然后我们的写入评论时通常是这样的:
$comment = new Comment(['body' => 'hello']);
$thread->comments()->save($comment);
或者这样最直接的方式:
$comment = Comment::create([
'commentable_type' => 'App\\Thread',
'commentable_id' => 1,
'body' => 'Very Good!',
]);
其实不管哪一种,我们都少不了表单验证,而且无论使用任何一种我们都得需要传入两个关键参数:类型
与id
,那就涉及到一个问题,如何验证呢?
你可能会写成这样:
public function store(Request $request)
{
$this->validate($request, [
'commentable_type' => 'required|string',
'commentable_id' => 'required|integer',
]);
$model = resolve($request->get('commentable_type'))
->find($request->get('commentable_id'));
if (!$model) {
abort(403, '目标对象不存在!');
}
$comment = new Comment(['body' => $request->get('body')]);
return new CommentResource($model->comments()->save($comment));
}
这样写感觉很复杂且不直观对吧?那么我们现在介绍一种拓展验证规则的写法:
首先我们在 AppServiceProvider 中注册一个验证规则 poly_exists
:
Validator::extend('poly_exists', function ($attribute, $value, $parameters, $validator) {
if (!$objectType = array_get($validator->getData(), $parameters[0], false)) {
return false;
}
try {
return !empty(resolve($objectType)->find($value));
} catch (\Exception $e) {
return false;
}
})
这样我们就可以把控制器里的写法简化成这样:
public function store(Request $request)
{
$this->validate($request, [
'commentable_id' => 'required|poly_exists:commentable_type',
]);
return new CommentResource(new Comment($request->all()));
}
怎么样?是不是简单很多,而且这样验证规则还能重用在其它同类多态关系的地方哦。
这样就结束了么?没有!
我们上面的拓展验证规则的写法没有感觉有些粗暴么?是时候规范一下了。
我们应该把所有的验证器都独立成一个类,放到 App\Validators
空间下,比如上面的关系验证我们可以叫做 App\Validators\PolyExistsValidator
:
<?php
namespace App\Validators;
/**
* Class PolyExistsValidator
*/
class PolyExistsValidator
{
public function validate($attribute, $value, $parameters, $validator)
{
if (!$objectType = array_get($validator->getData(), $parameters[0], false)) {
return false;
}
try {
return !empty(resolve($objectType)->find($value));
} catch (\Exception $e) {
\Log::error($e->getMessage());
return false;
}
}
}
然后我们在 AppServiceProvider 中添加一个属性 $validators
并且添加一个方法 registerValidators
:
protected $validators = [
'poly_exists' => \App\Validators\PolyExistsValidator::class,
];
/**
* Register validators.
*/
protected function registerValidators()
{
foreach ($this->validators as $rule => $validator) {
Validator::extend($rule, "{$validator}@validate");
}
}
public function boot()
{
$this->registerValidators();
}
在 boot 方法中我们统一注册了$validators
里的验证规则,这样一来,添加删除一个规则都会科学清晰很多了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
我一看这标题,二话不说拉到最下面就是一个赞。
给超哥点个赞 :+1:
@Soldoros 好一个二话不说
真是循序渐进啊,:+1:
写在request里面 controller注入request就可以实现了
@罪人 这个我当然知道,但是你要把这样的逻辑在多个 request copy paste 的时候恐怕不合适吧?
@overtrue 实现不一样 不好评价 告辞