利用 Laravel Macroable 特性优化多态参数传递的技巧分享
@这是小豪的第四篇文章
这篇文章主要是简单的给大家介绍一下如何利用 Laravel Macroable 特性优化多态参数传递,如果对多态关联这种类型不太熟悉的,建议先看一下《如何更快的找到自己所需的模型关联类型?》。设计思路来自超哥,我只是一个搬运工,哈哈,我是觉得这种实现方式太美妙了,得给大家分享一下。
准备
如果我们现在需要设计这样一个接口:获取指定文章的所有评论 Or 获取指定视频的所有评论。
我们有三张表:
视频表:videos
文章表:posts
评论表:comments
评论表中有这两个字段:commentable_type、commentable_id 分别存储评论主体信息。
他们之间的模型关系为多态关联,就不再多解释了,哈哈。
当接收到这个需求的时候,你可能会困惑,主体不确定该怎么去设计呢。通过 commentable_type 判断是什么模型,然后再根据确定的类型和 commentable_id 获取到具体的对象吗?此时你脑中的代码是什么样子的呢,反正当时我的脑子里面是一堆乱糟糟的代码,哈哈。现在就来给大家介绍这种优雅的实现方式。
获取可评论对象
首先我们利用 macro 给 Request 定义一个 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_type,commentable_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 协议》,转载必须注明作者和本文链接
关于 LearnKu
学习了
这个代码:
应该是:
吧?
@nff93 应该是:
我也写错啦,哈哈
@nff93 可能验证的时候传的是 commentable_type ,所以这样设计的话都可兼容,如果说直接验证 commentable_id 的话,直接用 value 是没问题的哈。
老兄

这里方便解释一下么,
$mode是怎么来的@野犭
@Lhao 我看你验证的时候验证的是
commentable_id字段,所以$value也行哈哈 :joy: