在Laravel的Model层做数据缓存的实现


Posted in PHP onSeptember 26, 2019

您在此之前可能就已经缓存过模型数据,但是我将向您展示一个使用动态记录模型的更精细的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 = \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() 方法的模型使用使用契约(接口)。

我希望你已经发现这个简单的技术是十分有用的!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
用PHP调用数据库的存贮过程!
Oct 09 PHP
PHP4实际应用经验篇(5)
Oct 09 PHP
php中防止恶意刷新页面的代码小结
Oct 31 PHP
CodeIgniter框架中_remap()使用方法2例
Mar 10 PHP
php实现建立多层级目录的方法
Jul 19 PHP
thinkphp学习笔记之多表查询
Jul 28 PHP
PHP自定session保存路径及删除、注销与写入的方法
Nov 18 PHP
PHP调用Linux命令权限不足问题解决方法
Feb 07 PHP
老司机传授Ubuntu下Apache+PHP+MySQL环境搭建攻略
Mar 20 PHP
phpMyAdmin无法登陆的解决方法
Apr 27 PHP
php实现往pdf中加数字签名操作示例【附源码下载】
Aug 07 PHP
Laravel5.5 数据库迁移:创建表与修改表示例
Oct 23 PHP
PHP的静态方法与普通方法用法实例分析
Sep 26 #PHP
tp5框架无刷新分页实现方法分析
Sep 26 #PHP
php判断目录存在的简单方法
Sep 26 #PHP
php 策略模式原理与应用深入理解
Sep 25 #PHP
php策略模式简单示例分析【区别于工厂模式】
Sep 25 #PHP
PHP 观察者模式深入理解与应用分析
Sep 25 #PHP
php模式设计之观察者模式应用实例分析
Sep 25 #PHP
You might like
PHP文件上传判断file是否己选择上传文件的方法
2014/11/10 PHP
php使用curl出现Expect:100-continue解决方法
2015/03/03 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
setTimeout 不断吐食CPU的问题分析
2009/04/01 Javascript
深入理解JavaScript系列(6) 强大的原型和原型链
2012/01/15 Javascript
DOM2非标准但却支持很好的几个属性小结
2012/01/21 Javascript
jquery入门—编写一个导航条(可伸缩)
2013/01/07 Javascript
showModalDialog模态对话框的使用详解以及浏览器兼容
2014/01/11 Javascript
alert出数组中的随即值代码
2014/09/25 Javascript
Windows系统下Node.js的简单入门教程
2015/06/23 Javascript
AngularJS入门教程之ng-class 指令用法
2016/08/01 Javascript
js-FCC算法-No repeats please字符串的全排列(详解)
2017/05/02 Javascript
JavaScript运动框架 解决速度正负取整问题(一)
2017/05/17 Javascript
BootStrap Table 后台数据绑定、特殊列处理、排序功能
2017/05/27 Javascript
Node.js学习之查询字符串解析querystring详解
2017/09/28 Javascript
jQuery仿移动端支付宝键盘的实现代码
2018/08/15 jQuery
Vue组件创建和传值的方法
2018/08/17 Javascript
js form表单input框限制20个字符,10个汉字代码实例
2019/04/12 Javascript
vue cli安装使用less的教程详解
2019/07/12 Javascript
JavaScript中的this基本问题实例小结
2020/03/09 Javascript
JavaScript实现猜数字游戏
2020/05/20 Javascript
[01:06:39]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第三局
2016/03/02 DOTA
[05:53]敌法师的金色冠名ID"BurNIng",是传说,是荣耀
2020/07/11 DOTA
python内存管理分析
2015/04/08 Python
使用Python脚本来控制Windows Azure的简单教程
2015/04/16 Python
简述:我为什么选择Python而不是Matlab和R语言
2017/11/14 Python
解决Pycharm运行时找不到文件的问题
2018/10/29 Python
django ModelForm修改显示缩略图 imagefield类型的实例
2019/07/28 Python
python爬虫 urllib模块url编码处理详解
2019/08/20 Python
python+selenium select下拉选择框定位处理方法
2019/08/24 Python
python如何调用java类
2020/07/05 Python
6种非常炫酷的CSS3按钮边框动画特效
2016/03/16 HTML / CSS
导出HTML5 Canvas图片并上传服务器功能
2019/08/16 HTML / CSS
2015年党员个人剖析材料
2014/12/18 职场文书
公司庆典欢迎词
2015/01/26 职场文书
涨价通知怎么写
2015/04/23 职场文书