Laravel Model 利用 Macroable 为数据模型添加宏能力。

简单的说一下宏能力,这个类是 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。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Seven 的代码太渣,欢迎关注我的新拓展包 medz/cors 解决 PHP 项目程序设置跨域需求。
本帖由系统于 4年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5
jcc123

原先用 octobercms 用这个扩展模型

UserModel::extend(function ($model){
            $model->hasMany['questions']=[Question::class, 'user_id', 'id'];
        });

laravel 的话,一直找没找到扩展模型的方法。感谢楼主

6年前 评论
medz

@jc91715 哈哈,没事没事。能用到就好。

6年前 评论

多谢楼主提点,特意看了源码,原来底层的Relations也是。之前还纳闷过,模型中定义的闭包里面,$this都是这个模型,到底在哪里做了bind,豁然开朗。
不过有一个不明白的点,有这样的需求应用场景,为什么需要动态去增加Relations

6年前 评论
medz

@Such丶 业务场景很简单,当你的系统很大的时候放在一起维护的话代码非常乱,或者类似 ts+ 的功能模块化设计 所有模块可卸载。当附加模块的时候关系自然附加,卸载模块的时候关系自然不存在 而且 尤其是在多态多对对的数据表设计中 可以直接从底层模型使用并且多个应用共用一个数据模型。如果你的程序比较单一当然用不到底。

6年前 评论

想要从配置文件,动态的对一个模型注入trait,有什么解决办法么

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
创始人 @ Odore Inc.
文章
33
粉丝
202
喜欢
532
收藏
198
排名:23
访问:24.7 万
私信
所有博文
社区赞助商