【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顺序重新排序,这样就乱了。

bannerAds