利用 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
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《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));

吧?

5年前 评论
finecho

@nff93 应该是:

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

我也写错啦,哈哈

5年前 评论
finecho

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

5年前 评论
野犭

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

5年前 评论
nff93

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

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!