一种缓存 Laravel 中 relation 的方法,欢迎讨论引申思考

目前这个方法只在直接访问属性时生效,使用方法同原 Laravel$model->user
使用的是 laravel 获取属性值时的魔术方法

    // 定义 relation
    public function user(){
        return $this->belongsTo(User::class);
    }

    // 缓存 relation
    public function getUserAttribute() {
        $key = '';
        $minutes = 0;
        return \Cache::remember($key, $minutes, function(){
            return $this->user()->getResults() ;
        });
    }

其他的更高级的缓存方式是可以继承一个 relation builder 的类然后重载 model 里的 relation 方法
例如

    // 继承类
    class BelongsTo extends \Illuminate\Database\Eloquent\Relations\BelongsTo {
    // 重载类中的方法以支持缓存
    // 或添加类似 $query->remeber($minutes) 的方法来进行扩展
    }

    // 在 model 中添加这个方法
    public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
    {
        // If no relation name was given, we will use this debug backtrace to extract
        // the calling method's name and use that as the relationship name as most
        // of the time this will be what we desire to use for the relationships.
        if (is_null($relation)) {
            list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);

            $relation = $caller['function'];
        }

        // If no foreign key was supplied, we can use a backtrace to guess the proper
        // foreign key name by using the name of the relationship function, which
        // when combined with an "_id" should conventionally match the columns.
        if (is_null($foreignKey)) {
            $foreignKey = Str::snake($relation).'_id';
        }

        $instance = new $related;

        // Once we have the foreign key names, we'll just create a new Eloquent query
        // for the related models and returns the relationship instance which will
        // actually be responsible for retrieving and hydrating every relations.
        $query = $instance->newQuery();
        // 这里不能直接加 remeber ,因为在 BelongsTo 中会进行 toBase() 操作再生成新的 $query ,添加了是无效的

        $otherKey = $otherKey ?: $instance->getKeyName();
        // 这里并不是 Laravel 中的 BelongsTo 类
        return new \App\BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
    }

欢迎大家讨论这种缓存方式的可用性以及易用性,以及更好的缓存 relation 的方法

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 10

这样做的意义不大吧?

使用Cache::remeber直接缓存包括relation的整条查询似乎更好,因为它不会对relation本身做缓存

7年前 评论

@匿名用户 使用场景是这样的,在一页查询中大量出现指向同一个关联目标的时候,会多次查询这个关联,如果不进行缓存的话会出现重复查询,而使用缓存能避免这一现象

7年前 评论

@clcy1243 有点没看懂,直接通过预加载关系然后把这个模型存进去不行吗?预加载就是避免多次查询的吧

7年前 评论

@oustn 假设一个这样的场景,一个订单列表页面,有多个订单由同一个用户产生,在调用 $order->user 时,会由多个订单发起对同一个用户的查询,这种时候就会产生重复的查询。

在 Laravel 中,一个对象只有在第一次加载关系属性时才会进行查询,之后会直接从relations 数组中取出来,这个时候缓存是没有意义的,但是多对一关系中 Laravel 并不会自动缓存,而是分别查询,我的缓存是为了这种场景做的,而且可以和 Model::find($id) 使用同一个 key 。

7年前 评论

@clcy1243 了解你的意思了。

但是用with关系的话就算是多条记录对应一条也是查询一次,生成的查询语句应该是像这样的:

select * from `users` where `users`.`id` in (?, ?, ?) 1,2,3'

你可以用 DB::listen 监听一下查询语句。

7年前 评论

@oustn 如果有多级关系的查询该怎么使用 with 呢?

7年前 评论

@clcy1243 可以用. 符号,比如

$order = Order::with('user.addresses')->find(10);

这样查询出来的结果就是一个嵌套的,通过$order->user->addresses访问。 嵌套预加载

7年前 评论

@oustn 之前一直以为预加载的 dot 只能用在relation model本身的属性上做条件查询用,不知道还能访问 relationrelation ,多谢啦

7年前 评论

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