利用 Laravel Macroable 特性優化多型引數傳遞的技巧分享

finecho發表於2018-12-28

@這是小豪的第四篇文章
這篇文章主要是簡單的給大家介紹一下如何利用 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) ?? $mode;

        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

相關文章