Laravel 数量统计优化
Laravel 数量统计优化
有很多如下类似的需求,在一个博客系统,博客有分类,分类有个博客数量的字段 post_count
,通常会在博客被发布后统计并存入这个字段。
我不清楚大佬们怎么做的,反正我一般是在分类模型中定义一个这样的方法:
public function freshPostCount()
{
$this->post_count = $this->posts()->count();
return $this->save();
}
非常清晰,非常简单!
但是有个问题,如果有并发存在,可能会导致数据不一致,或者说不准确。作为一个有追求的渣渣,我想做点什么来尽量降低不一致性的发生。
首先想到的是,用原生 SQL
:
public function freshPostCount()
{
\DB::update('UPDATE `pre_cates` cate SET `post_count` = (
SELECT count( * ) FROM `pre_posts` post WHERE `post`.`cate_id` = `cate`.`id` AND `post`.`is_published` = 1 )
WHERE `id` = '. $this->getKey());
}
看上去能解决点问题,但看着有点恶心,格格不入,毫无优雅可言。而且所有东西都写死了,非常没有弹性,那就让他弹起来。
从上面的解决方案来看,统计和赋值只要执行一次就可以,那么如此:
public function freshPostCount()
{
$this->update([
'post_count' => 'SELECT count( * ) FROM `pre_posts` WHERE `cate_id` = '. $this->getKey() .' `is_published` = 1'
]);
}
但是执行不起作用,发现框架把 SQL
语句绑定成参数,作为字符串赋值了。于此同时我发现 Illuminate\Database\Query\Expression
类的存在,稍作修改:
public function freshPostCount()
{
$this->update([
'post_count' => new Expression('(SELECT count( * ) FROM `pre_posts` WHERE `cate_id` = '. $this->getKey() .' `is_published` = 1)'),
]);
}
可以执行,并且还把 updated_at
自动带上了,但依然留有遗憾,能否利用 posts()
这个关联关系,答案是可以的,不过比较麻烦:
public function freshPostCount()
{
$count_sql = $this->posts()->published()->selectRaw('count(*)')->toRawSql();
return $this->update([
'post_count' => new Expression("($count_sql)"),
]);
}
toRawSql()
是一个自定义的方法,获取完整的执行 SQL:
\Illuminate\Database\Query\Builder::macro('toRawSql', function(){
return array_reduce($this->getBindings(), function($sql, $binding){
return preg_replace('/\?/', is_numeric($binding) ? $binding : "'{$binding}'", $sql, 1);
}, $this->toSql());
});
\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
return $this->getQuery()->toRawSql();
});
不知道各位大佬都是怎么操作的,一起交流下啊
本作品采用《CC 协议》,转载必须注明作者和本文链接
? 我的导航网站已经可以公开使用啦:Cootab ✨
推荐文章: