在Laravel的Model层做数据缓存的实现
您在此之前可能就已经缓存过模型数据,但是我将向您展示一个使用动态记录模型的更精细的Laravel模型缓存技术,这是我一开始在RailsCasts学习到的技术。
使用模型的唯一缓存键,您可以缓存模型(或关联模型)更新时自动更新(以及缓存失效)的模型上的属性和关联,一个好处是访问缓存的数据比在控制器中缓存的数据更具可复用性,因为它在模型上而不是在单个控制器方法中。
这是这个技术的要点:
假设你有很多个 Comment
的Article
模型,给定下面的Laravel blade 模板,你就可以像下面这样访问/article/:id
路由时得到评论的数量:
<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 方法看看。
模型的触发
可以通过使用模型的touch()
方法来更新文章的updated_at
列值:
$ php artisan tinker
$article = AppArticle::first();
= AppArticle {#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 AppArticle;
use IlluminateDatabaseEloquentModel;
class Comment extends Model
{
protected $guarded = [];
protected $touches = ['article'];
public function article()
{
return $this- belongsTo(Article::class);
}
}
这里的$touches
属性是个数组,包含了在评论的创建、保存和删除时会引起“触发”的关联信息。
缓存的属性
我们先回到$article- cached_comments_count
访问器。该方法的实现可能象AppArticle
模型中的样子:
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 AppComment;
use IlluminateSupportFacadesCache;
use IlluminateDatabaseEloquentModel;
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 AppArticle;
use IlluminateDatabaseEloquentModel;
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()
方法的模型使用使用契约(接口)。
我希望你已经发现这个简单的技术是十分有用的!
以上就是本文的全部内容,希望对大家的学习有所帮助。
- 【SQLServer】记一次数据迁移-标识重复的简单处理
- Django用户登录与注册系统
- 洛谷P1450 [HAOI2008]硬币购物
- 一个完整的Django入门指南(二)
- 一个完整的Django入门指南(三)
- 1.Django自学课堂 模板的使用
- 23.Django基础
- SpringBoot开发案例之整合Quartz注入Service
- 谈谈代码质量问题
- SpringCloud config配置文件加密
- SpringCloud服务比较快的下线配置
- 洛谷P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold
- 24.Django路由规则
- 洛谷P2852 [USACO06DEC]牛奶模式Milk Patterns
- php概述
- php教程
- php环境搭建
- PHP书写格式
- php变量
- php常量
- PHP注释
- php数组
- php字符串 string
- PHP整型 integer
- PHP浮点型 float
- php布尔型
- php数据类型之数组
- php数据类型之对象
- php数据类型之null
- php数据类型之间的转换
- php运算符
- php表达式
- PHP循环控制
- PHP流程控制
- php函数
- php全局变量
- PHP魔术变量
- php命名空间
- php 日期
- PHP包含文件
- php文件
- PHP 文件上传
- php Cookies
- php Sessions
- php email
- php安全email
- php错误处理
- PHP异常处理
- php过滤器
- PHP 高级过滤器
- php json
- php 表单
- PHP MySQL 简介
- PHP 连接 MySQL
- php创建数据库
- php 创建表
- php mysq 插入数据
- PHP MySQL 插入多条数据
- PHP MySQL 预处理语句
- php mysql 读取数据
- php mysql where
- PHP MySQL Order By
- PHP MySQL Update
- PHP MySQL Delete
- php ODBC
- 3分钟短文 | Laravel 内3种数据校验的写法,你喜欢哪一个?
- 纯JavaScript实现的MQTT智能门锁
- 3分钟短文 | Laravel 灵活地获取当前请求的路由地址
- 云原生安全 | docker容器逃逸
- 字节数组X中存放着 0~F共16个十六进制数,请将这些数以十六进制形式显示在屏幕上。
- ubuntu 16安装asp.net
- MYSQL Rewriter plugin 我那什么拯救你,垃圾系统
- 不使用华为om工具如何手工部署openGauss主从流复制环境
- 比较两个等长的字符串,若相同,则输出Match!,若不同,则输出No Match!
- 逻辑回归算法原理及实现
- Python链表详细笔记
- 26个你需要学习的Firefox配置技巧,改进体验和加快浏览器响应速度
- Softmax算法原理及实现
- Android Activity 活动的生命周期
- Android学习笔记,不断更新