Laravel 模型缓存

本文原文来自laravel-news,代码在github

之前可能已经在控制器中缓存了一些模型数据,但是我会告诉你一个laravel模型缓存技术,它使用活动记录模型更精细一些。

使用模型上的唯一缓存键,可以缓存模型(或相关模型)更新时自动更新(以及缓存失效)的模型上的属性和关联。

一个好处是访问缓存的数据比在控制器中缓存数据更具可移植性,因为它在模型上而不是在单个控制器方法中。

假设现在有一个有许多评论模型的文章模型。

给出以下的laravel blade模板,你可以像在你的/articles路径上那样检索评论计数:

<h3>$article->comments->count() {{ str_plural('Comment', $article->comments->count())</h3>

可以在控制器中缓存评论计数,但是当有多个需要缓存的一次性查询和数据时,控制器会变得非常难看。

使用控制器,访问缓存的数据也不是很方便。

我们可以构建一个模板,只有在文章更新时才能访问数据库,任何有权访问模型的代码都可以获取缓存的值:

<h3>$article->cached_comments_count {{ str_plural('Comment', $article->cached_comments_count)</h3>

使用模型访问器,我们将根据文章上次更新的时间缓存评论计数。

那么当新评论被添加或删除时,我们如何更新文章的updated_at列呢?

答案是配置touch方式。

Touching 模型

使用模型的touch()方法,我们可以更新文章的updated_at列:

$ php artisan tinker

>>> $article = \App\Article::first();
=> App\Article {#746
     id: 1,
     title: "Hello World",
     body: "The Body",
     created_at: "2018-01-11 05:16:51",
     updated_at: "2018-01-11 05:51:07",
   }
>>> $article->updated_at->timestamp
=> 1515649867
>>> $article->touch();
=> true
>>> $article->updated_at->timestamp
=> 1515650910

我们可以使用更新后的时间戳来使缓存无效,但是当我们添加或删除评论时,我们如何触摸文章的updated_at字段?

恰恰恰巧,Eloquent模型有一个名为$touches的属性。以下是我们的评论模型可能的样子:

<?php

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

$touches属性是一个包含关联的数组,当评论被创建,保存或删除时,该关联将会被“触及”。

缓存属性

让我们回到$article->cached_comments_count访问器。在app\article模型中实现可能如下所示:

public function getCachedCommentsCountAttribute()
{
    return \Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
        return $this->comments->count();
    });
}

我们使用唯一的cachekey()方法将模型缓存15分钟,并简单地返回闭包内的注释计数。

请注意,我们也可以使用\Cache::rememberforever()方法,并依靠我们的缓存机制的垃圾收集来删除旧key。我已经设置了一个计时器,以便缓存将在大部分时间点击,每十五分钟一次新的缓存。

cachekey()方法需要使模型唯一,并在模型更新时使缓存失效。这是我的快捷键实现:

public function cacheKey()
{
    return sprintf(
        "%s/%s-%s",
        $this->getTable(),
        $this->getKey(),
        $this->updated_at->timestamp
    );
}

模型的cachekey()方法的示例输出可能会返回以下字符串表示形式:

articles/1-1515650910

关键是表的名称,模型ID和当前updated_at时间戳。一旦我们触摸模型,时间戳将被更新,我们的模型缓存将被适当地失效。

完整的Article模型内容如下:

<?php

namespace App;

use App\Comment;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public function cacheKey()
    {
        return sprintf(
            "%s/%s-%s",
            $this->getTable(),
            $this->getKey(),
            $this->updated_at->timestamp
        );
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function getCachedCommentsCountAttribute()
    {
        return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
            return $this->comments->count();
        });
    }
}

和相关的评论模型:

<?php

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

下一步是什么?

我已经告诉你如何缓存一个简单的评论计数,但是如何缓存所有的评论?

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments;
    });
}

您也可以选择将注释转换为数组,而不是序列化模型,只允许简单的数组访问前端数据:

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments->toArray();
    });
}

最后,我在文章模型中定义了cachekey()方法,但是您希望通过一个叫做 ProvidesModelCachekey 的 trait 来定义这个方法,您可以在多个模型上使用这个方法,或者在我们所有模型扩展的基本模型上定义方法。

您甚至可能希望为实现cachekey()方法的模型使用合同(接口)。

Copyright © http://blog.webfsd.com 2018 all right reserved,powered by Gitbook该文件修订时间: 2019-05-21 04:55:26

results matching ""

    No results matching ""