Laravel Model 利用 Macroable 為資料模型新增巨集能力。

medz發表於2017-08-03

簡單的說一下巨集能力,這個類是 Illuminate\Support\Traits\Macroable 其中利用過載實現了可以定義巨集的功能,通俗一點江,就是通過 macro 靜態方法新增回撥,並定義一個名字,利用 __call 噹噹前類沒有這個函式的時候執行這個函式名註冊的回撥。

產生需求

在使用 Laravel 開發 ThinkSNS Plus 的時候,因為很多功能塊都沒有寫在一個庫裡面,利用 擴充包 的形式新增實際功能,裡面很多地方也用到了 “多型多對多” 的關係,問題來了,我開發一個問答程式,想要給使用者模型增加發布的問題或者回答的關係,起初是繼承一份 User 模型,新增了關係,之後就發現問題了,因為使用者的 tag 是使用多型多對多的關係,我通過繼承的使用者模型是無法拿到這種關係資料的因為 ***able_type 是 user 資料模型類名稱或者別名。而我繼承之後類也就發生改變了。

完成需求

隨之想到,在 Laravel 中有一個 Trait 叫做 Macroable 然後發現 Builder 都有這種能力,而 Model 沒有,隨之也將這個 Trait 新增到要使用的model上,後來發現,如果其他模型也要用是不是也要再新增一次?隨之寫了一份 Trait :

trait Macroable
{
    use \Illuminate\Support\Traits\Macroable {
        __call as macroCall;
    }

    /**
     * Get a relationship value from a method.
     *
     * @param string $key
     * @return mixed
     * @author Seven Du <shiweidu@outlook.com>
     */
    public function getRelationValue($key)
    {
        $relation = parent::getRelationValue($key);
        if (! $relation && static::hasMacro($key)) {
            return $this->getRelationshipFromMethod($key);
        }

        return $relation;
    }

    /**
     * Handle dynamic method calls into the model.
     *
     * @param string $method
     * @param array $parameters
     * @return mixed
     * @author Seven Du <shiweidu@outlook.com>
     */
    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        return parent::__call($method, $parameters);
    }

    /**
     * Handle dynamic static method calls into the method.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public static function __callStatic($method, $parameters)
    {
        return parent::__callStatic($method, $parameters);
    }
}

只要在要使用的 model 中 use 即可。

使用

有了這個 Trait 那麼我們新增到 User 模型中,就可以使用巨集能力為其動態新增函式了:

User::macro('questions', function () {
    return $this->hasMany(Question::class, 'user_id', 'id');
});

這樣,我們可以直接 :

$questions = $user->questions;

拿到使用者釋出的所有問題了。


開源不易,https://github.com/slimkit/thinksns-plus 求 star。

Seven 的程式碼太渣,歡迎關注我的新擴充包 medz/cors 解決 PHP 專案程式設定跨域需求。

相關文章