Laravel 自增浏览数实现(数据库 + Redis)
自增浏览数
最近一个项目用到自增浏览数,首先考虑到的就是在数据表中加个 view_count
字段,并在每次访问时 increment
自增。日复一日,每次访问执行 update
,对性能是十分不利的。
看了 9.4. 用户最后登录时间《L02 Laravel 教程 - Web 开发实战进阶 ( Laravel 5... ,就使用 Redis + 数据库 实现了类似的功能。
数据库字段
首先需要在数据表中加入一个 view_count
字段:
$table->unsignedInteger('view_count')->default(0);
增加 ViewCountsHelper Trait
废话不多说,直接上代码,都有完整的注释。思路来自 https://github.com/summerblue/larabbs/tree... 。
<?php
namespace App\Models\Traits;
use Redis;
use Carbon\Carbon;
trait ViewCountsHelper {
// 缓存相关
protected $hash_prefix = 'topic_view_counts_';
protected $field_prefix = 'topic_';
public function viewCountIncrement()
{
// 获取今日 Redis 哈希表名称,如:topic_view_counts_2017-10-21
$hash = $this->getHashFromDateString(Carbon::now()->toDateString());
// 字段名称,如:topic_1
$field = $this->getHashField();
// 当前阅读数,如果存在就自增,否则就为 1
$count = Redis::hGet($hash, $field);
if ($count) {
$count++;
} else {
$count = 1;
}
// 数据写入 Redis ,字段已存在会被更新
Redis::hSet($hash, $field, $count);
}
public function syncTopicViewCounts()
{
// 获取昨日的哈希表名称,如:topic_view_counts_2017-10-21
$hash = $this->getHashFromDateString(Carbon::now()->toDateString());
// 从 Redis 中获取所有哈希表里的数据
$counts = Redis::hGetAll($hash);
// 如果没有任何数据直接 return
if (count($counts) === 0) {
return;
}
// 遍历,并同步到数据库中
foreach ($counts as $topic_id => $view_count) {
// 会将 `topic_1` 转换为 1
$topic_id = str_replace($this->field_prefix, '', $topic_id);
// 只有当话题存在时才更新到数据库中
if ($topic = $this->find($topic_id)) {
$topic->view_count = $this->attribute['view_count'] + $view_count;
$topic->save();
}
}
// 以数据库为中心的存储,既已同步,即可删除
Redis::del($hash);
}
public function getViewCountAttribute($value)
{
// 获取今日对应的哈希表名称
$hash = $this->getHashFromDateString(Carbon::now()->toDateString());
// 字段名称,如:topic_1
$field = $this->getHashField();
// 三元运算符,优先选择 Redis 的数据,否则使用数据库中
$count = Redis::hGet($hash, $field) ? : $value;
// 如果存在的话,返回 数据库中的阅读数 加上 Redis 中的阅读数
if ($count) {
return $this->attribute['view_count'] + $count;
} else {
// 否则返回 0
return 0;
}
}
public function getHashFromDateString($date)
{
// Redis 哈希表的命名,如:topic_view_counts_2017-10-21
return $this->hash_prefix . $date;
}
public function getHashField()
{
// 字段名称,如:topic_1
return $this->field_prefix . $this->id;
}
}
然后在需要此功能的模型中 use Traits\ViewCountsHelper
即可。
每天将浏览量同步到数据库
app/Console/Commands/SyncTopicViewCounts.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Topic;
class SyncTopicViewCounts extends Command
{
protected $signature = 'topic:sync-topic-view-counts';
protected $description = '将话题 view_count 从 Redis 同步到数据库中';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(Topic $topic)
{
$topic->syncTopicViewCounts();
$this->info("同步成功!");
}
}
app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// ...
$schedule->command('topic:sync-topic-view-counts')->dailyAt('00:00');
}
使用
<?php
namespace App\Http\Controllers;
use App\Models\Topic;
use Illuminate\Http\Request;
class TopicsController extends Controller
{
public function show(Topic $topic)
{
$topic->viewCountIncrement(); // 自增浏览数
dd($topic->view_count); // 获取浏览数
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: