利用 Laravel Macroable 特性优化多态参数传递的技巧分享

@这是小豪的第四篇文章
这篇文章主要是简单的给大家介绍一下如何利用 Laravel Macroable 特性优化多态参数传递,如果对多态关联这种类型不太熟悉的,建议先看一下《如何更快的找到自己所需的模型关联类型?》。设计思路来自超哥,我只是一个搬运工,哈哈,我是觉得这种实现方式太美妙了,得给大家分享一下。

准备

如果我们现在需要设计这样一个接口:获取指定文章的所有评论 Or 获取指定视频的所有评论。

我们有三张表:
视频表:videos
文章表:posts
评论表:comments
评论表中有这两个字段:commentable_type、commentable_id 分别存储评论主体信息。
他们之间的模型关系为多态关联,就不再多解释了,哈哈。

当接收到这个需求的时候,你可能会困惑,主体不确定该怎么去设计呢。通过 commentable_type 判断是什么模型,然后再根据确定的类型和 commentable_id 获取到具体的对象吗?此时你脑中的代码是什么样子的呢,反正当时我的脑子里面是一堆乱糟糟的代码,哈哈。现在就来给大家介绍这种优雅的实现方式。

获取可评论对象

首先我们利用 macroRequest 定义一个 commentable

Request::macro('commentable'), function(bool $required = false){ if (!\request()->has('commentable_type')) { return $required ? \abort(422, '目标对象不存在') : null; } $model = \request()->get('commentable_type'); $model = Relation::getMorphedModel($model) ?? $mode; $commentable = \call_user_func([$model, 'find'], \request()->get('commentable_id')); if (!$commentable){ return $required ? \abort(422, '目标对象不存在') : null; } return $commentable; });

可以看到,目标对象的转换就是通过 commentable_typecommentable_id 这两个参数来的,这段代码不是很难,大家研究一下就看懂哒。可以在 服务提供者 中定义。

控制器

class CommentController extends Controller { /** * Display a listing of the resource. * * @param \Illuminate\Http\Request $request * * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection */ public function index(Request $request) { return CommentResource::collection($request->commentable(true)->comments()->get()); } }

大家会发现现在已经可以通过 $request->commentable(true) 来获取可评论对象了,但是少了验证,但是这个验证该怎么写呢,现在我们来看一下。

验证规则

class Polymorphic extends Rule { /** * @var string */ protected $name; /** * @var string */ protected $message; /** * Create a new rule instance. * * @param string $name * @param string|null $message */ public function __construct(string name, string $message = null) { $this->name = $name; $this->message = $message; } /** * Determine if the validation rule passes. * * @param string $attribute * @param mixed $value * * @return bool */ public function passes($attribute, $value) { $model = request()->get(\sprintf('%s_type'), $this->name); $model = Relation::getMorphedModel($model) ?? $model; return \class_exists($model) && (bool) \call_user_func([$model, 'find'], \request()->get(\sprintf('%s_id'), $this->name))); } /** * Get the validation error message. * * @return string */ public function message() { return $this->message ?: '未指定目标对象或目标不存在'; } }

现在规则定义好了,我们来使用:

 /** * Display a listing of the resource. * * @param \Illuminate\Http\Request $request * * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection * * @throws \Illuminate\Validation\ValidationException */ public function index(Request $request) { $this->validate($request, [ 'commentable_id' => ['required', new Polymorphic('commentable', '未指定评论对象或对象不存在')], ]); return CommentResource::collection($request->commentable(true)->comments()->get()); }

结束语

是不是很简单很优雅呀,哈哈。这种方式我觉得忒棒了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
finecho # Lhao
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7
nff93

这个代码:

return \class_exists($model) && (bool) \call_user_func([$model, 'find'], \request()->get(\sprintf('%s_type'), $this->name)));

应该是:

return \class_exists($model) && (bool) \call_user_func([$model, 'find'], $value));

吧?

7年前 评论
finecho

@nff93 应该是:

return \class_exists($model) && (bool) \call_user_func([$model, 'find'], \request()->get(\sprintf('%s_id'), $this->name)));

我也写错啦,哈哈

7年前 评论
finecho

@nff93 可能验证的时候传的是 commentable_type ,所以这样设计的话都可兼容,如果说直接验证 commentable_id 的话,直接用 value 是没问题的哈。

7年前 评论
野犭

老兄
file
这里方便解释一下么,$mode 是怎么来的

7年前 评论
nff93

@Lhao 我看你验证的时候验证的是 commentable_id 字段,所以 $value 也行哈哈 :joy:

7年前 评论