文章转发自专业的Laravel开发者社区,原始连接:learnku.com/laravel/t/7…php
您在此以前可能就已经缓存过模型数据,可是我将向您展现一个使用动态记录模型的更精细的Laravel模型缓存技术,这是我一开始在 RailsCasts学习到的技术。前端
使用模型的惟一缓存键,您能够缓存模型(或关联模型)更新时自动更新(以及缓存失效)的模型上的属性和关联,一个好处是访问缓存的数据比在控制器中缓存的数据更具可复用性,由于它在模型上而不是在单个控制器方法中。laravel
这是这个技术的要点:数据库
假设你有不少个 Comment
的 Article
模型,给定下面的Laravel blade 模板,你就能够像下面这样访问 /article/:id
路由时获得评论的数量:数组
<h3>$article->comments->count() {{ str_plural('Comment', $article->comments->count())</h3>
复制代码
您能够在控制器中缓存评论的计数,可是当您有多个须要缓存的一次性查询和数据时,控制器会变得很是臃肿难看。使用控制器,访问缓存的数据也不是很方便。缓存
咱们能够构建一个模板,它仅在文章更新时访问数据库,而且访问该模型的全部代码均可以获取缓存值:bash
<h3>$article->cached_comments_count {{ str_plural('Comment', $article->cached_comments_count)</h3>
复制代码
经过使用模型访问器,咱们能够缓存基于最后一次文章更新的评论计数值。闭包
所以,在评论新增或删除时咱们该怎么更新文章的 updated_at
列值呢?ide
先进入 touch 方法看看。学习
能够经过使用模型的 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
复制代码
咱们能够用更新的 timestamp 值使缓存失效。不过在新增或删除一个评论时,咱们怎么触发修改文章的 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()
方法,靠着缓存机制的垃圾回收策略以删除过时的键值。我设置了一个定时器,以便在每隔 15 分钟的缓存刷新间隔里,缓存可在该时间的多数范围内有最高的命中率。
cacheKey()
方法要用到模型的惟一键值,而且在模型更新时对应缓存失效。下面是个人 cacheKey
实现代码:
public function cacheKey()
{
return sprintf(
"%s/%s-%s",
$this->getTable(),
$this->getKey(),
$this->updated_at->timestamp
);
}
复制代码
模型的 cacheKey()
方法示例输出结果可能返回下面的字串信息:
articles/1-1515650910
复制代码
这个键值是由表名、模型id值及当前 updated_at
的 timestamp 值组成。一旦咱们触发这个模型,timestamp 值就会更新,而且咱们的模型缓存就会相应地失效。
如下是 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();
});
}
}
复制代码
而后是关联的 Comment
模型:
<?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();
});
}
复制代码
最后, 我在 Article
模型中定义了cacheKey()
方法,可是你可能想要经过一个名为 ProvidesModelCacheKey
的trait来定义这个方法以便你能够在复合模型中使用或者在一个基础模型中定义全部模型扩展的方法。 你甚至可能想要为实现cacheKey()
方法的模型使用使用契约(接口)。
我但愿你已经发现这个简单的技术是十分有用的!