【Laravel】使用Redis实现浏览量排行功能
这是使用Redis来管理缓存,创建排行榜功能时的备注。
Laravel 5.8 Redis:Laravel 5.8 的 Redis 组件
环境如下:
– Laravel 5.8
– PHP 7.2
– Predis 1.1
安装Redis
为了使用Redis,我们需要安装Redis的PHP扩展。在Laravel 5.8中,我们使用predis,但在’6.0’中,predis已经被弃用了。所以在Laravel 6.x中,我们应该使用phpredis。(本文不涉及phpredis)
使用composer安装predis库。
在composer中安装predis/predis包。
$ composer require predis/predis
Redis的配置
编辑数据库的配置文件。在我的情况下,由于我在docker容器中运行redis,所以主机名是容器名。
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', 'redis'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
]
CACHE_DRIVER=redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
确认操作
我将使用Tinker来进行操作确认。
$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.3.14 — cli) by Justin Hileman
>>> use Redis
>>> Redis::set('test', 'TEST');
=> Predis\Response\Status {#3050}
>>> Redis::get('test')
=> "TEST"
设计
将功能大致分为两个部分
将文章的浏览次数保存到缓存中:
– 使用文章的id作为键,保存浏览次数作为值。
– 如果有文章被浏览:
– 如果已经存在该键的保存,则将浏览次数加1(值+1)。
– 如果还没有该键的保存,则创建一个新的键,并保存值为1。
从缓存中获取排名结果,并以数组形式返回
·获取所有键,并以值的降序对数组进行排序,然后返回。
排名服务
将排名功能作为RankingService类提取出来,并作为一个库处理。
<?php
namespace app\Libraries;
use Illuminate\Http\Request;
use Redis;
class RankingService
{
//keyの保存、閲覧数のインクリメント
public function incrementViewRanking($id)
{
$key = "ranking-"."id:".$id;
$value = Redis::get($key);
if(empty($value)){
Redis::set($key, "1");
Redis::expire($key, 3600*24);
} else {
Redis::set($key, $value + 1);
}
}
//ランキング結果を配列で取得
public function getRankingAll()
{
$keys = Redis::keys('ranking-*');
$results = Array();
if(empty($keys) != true){
for($i = 0; $i < sizeof($keys); $i++){
$id = explode(':', $keys[$i])[1];
$point = Redis::get('ranking-id:'. $id);
$results[$id] = $point;
}
arsort($results, SORT_NUMERIC);
}
return $results;
}
}
保存关键字、增加浏览次数
public function incrementViewRanking($id)
{
$key = "ranking-"."id:".$id;
$value = Redis::get($key);
if(empty($value)){
Redis::set($key, "1");
Redis::expire($key, 3600*24);
} else {
Redis::set($key, $value + 1);
}
}
在incrementViewRanking函数中,通过接受参数作为文章的id,进行浏览量的管理。逐个查看。
$key = "ranking-"."id:".$id;
$value = Redis::get($key);
通过键获取值。
可以使用Redis::get()在Redis中获取值。
if(empty($value)){
Redis::set($key, "1");
Redis::expire($key, 3600*24);
} else {
Redis::set($key, $value + 1);
}
如果存在key,则将value递增。
如果不存在,则将文章的id保存为ranking-id:$id作为键。
使用Redis::expire()来指定缓存时间。本次设定为3600秒×24,即24小时。
获取排名结果
public function getRankingAll()
{
$keys = Redis::keys('ranking-*');
$results = Array();
if(!empty($keys)){
for($i = 0; $i < sizeof($keys); $i++){
$id = explode(':', $keys[$i])[1];
$point = Redis::get('ranking-id:'. $id);
$results[$id] = $point;
}
arsort($results, SORT_NUMERIC);
}
return $results;
}
在getRankingAll函数中,将id和浏览数按照排名顺序存储在数组中。
对保存的所有键进行arsort()函数排序,按照值的降序排列。
Redis::keys(‘ranking-*’)是用于获取key的方法。可以使用*来指定通配符。
这个地方实际上有些混乱,所以有改善的空间。
在各个控制器中的使用
增加浏览数
每次显示文章时都进行递增操作,所以incrementViewRanking()会在控制器的显示方法中使用。
public function show(Article $article)
{
$ranking = new RankingService;
$ranking->incrementViewRanking($article->id); //インクリメント
$movie->getArticle($article->id); //記事の取得
return view('articles.show', [
'article' => $article
]);
}
获取排名
public function ranking(Article $article)
{
$ranking = new RankingService;
$results = $ranking->getRankingAll();
$article_ranking = $article->getArticleRanking($results);
return view('article.ranking', [
'article_ranking' => $article_ranking
]);
}
从通过getRankingAll()获取的排名数组中获取文章。
public function getArticleRanking(Array $results)
{
$article_ids = array_keys($results);
$ids_order = implode(',', $article_ids);
$article_ranking = $this->whereIn('id', $article_ids)
->orderByRaw(DB::raw("FIELD(id, $ids_order)"))
->paginate(10);
return $article_ranking;
}
我想按照数组的顺序获取文章,所以我决定这样写原始查询。如果只是用whereIn普通地获取,Laravel会自动按照id顺序重新排序,这样就乱了。