给帖子发表回复后更新帖子回复总数的5种方式

我们常常有类似这样的需求,比如一个论坛,有帖子 topic,有回复 comment,在帖子下发布回复后我们常常需要在帖子表 topic 的 comment_count 字段 + 1,这样我们就可以在读取帖子详情时直接知道这个帖子有多少回复,而不需要对 comment 表做 count 查询,这样带来的好处不仅是方便,而且性能更好,比如帖子列表页一次性列出 50 条帖子,如果分别做 50 次 count 查询显然不现实,在次总结 5 种实现方式。

topicID 标题 回帖数 浏览数
1003 消息不可靠 12 203
1001 站长是基佬 60 856

在 Laravel 中,如果你想要实现在添加 comment 表记录时,自动更新对应 topic 表的 comment_count 列,可以采用以下几种方法:

1. 使用模型事件(Model Events)#

Comment 模型中使用 created 事件来更新 topiccomment_count

class Comment extends Model
{
    public static function boot()
    {
        parent::boot();

        static::created(function ($comment) {
            // 这里的$comment->topic是假设模型有关联
            // 这里直接+1, 也可以做count
            // 并发会让这种方式不一定精确,有精确需求的请加锁或者考虑其他方式
            // 有删除帖子功能别忘记一样的操作
            $topic = $comment->topic;
            $topic->comment_count++;
            $topic->save();
        });
    }
}

2. 使用本地数据库触发器(Database Trigger)#

在数据库层面创建一个触发器,当向 comment 表插入新记录后自动更新 topic 表的 comment_count

DELIMITER //
CREATE TRIGGER after_comment_insert
AFTER INSERT ON comment
FOR EACH ROW
BEGIN
    UPDATE topic
    SET comment_count = comment_count + 1
    WHERE id = NEW.topic_id;
END; //
DELIMITER ;

3. 使用 Laravel 的监听器(Listeners)#

创建一个事件监听器,当 Comment 被创建后,监听器将处理更新 topic 的逻辑。

// Event: CommentCreated
class CommentCreated
{
    public $comment;

    public function __construct(Comment $comment)
    {
        $this->comment = $comment;
    }
}

// Listener: UpdateTopicCount
class UpdateTopicCount
{
    public function handle(CommentCreated $event)
    {
        // 同方案1一样,想精确得加锁
        $event->comment->topic->comment_count++;
        $event->comment->topic->save();
    }
}

然后在 Comment 模型的 created 事件中触发这个事件:

use App\Events\CommentCreated;

static::created(function ($comment) {
    event(new CommentCreated($comment));
});

4. 利用数据库的级联更新#

如果你使用的是支持外键级联操作的数据库系统(如 PostgreSQL),可以设置外键约束来自动更新 comment_count

ALTER TABLE comment
  ADD CONSTRAINT comment_topic_id_fkey
  FOREIGN KEY (topic_id)
  REFERENCES topic(id)
  ON INSERT UPDATE topic.comment_count;

请注意,并非所有的数据库系统都支持这种操作。

5. 手动更新#

虽然不是自动的,但你也可以选择在添加评论后手动更新 topiccomment_count

复制

$comment = new Comment();
// ... 填充 $comment 数据并保存
$topic = Topic::find($comment->topic_id);
$topic->comment_count++;
$topic->save();
本作品采用《CC 协议》,转载必须注明作者和本文链接
:blush: wink
唐章明
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 7

所以这几种性能如何呢~

10个月前 评论

总结呢 :see_no_evil:

10个月前 评论
sanders 10个月前
Lee丶 (作者) 10个月前

所以这种基本操作使用过程真的合理么?这点性能可以忽略不计,但是搞不好这真的就成了祖传逻辑了。

10个月前 评论

还有一个 observer 的方式,其实等同于 eventlistener 方式,本质都是观察者模式。
Laravel Observer
这几种方式,主要就是数据库层面与代码层面实现的区别:

  1. 性能:数据库触发器理论上比业务的触发器要好。
  2. 维护:在代码层面实现,可以自主维护,后续人员接手方便,在数据库层面,维护相对代码来说麻烦些,维护人员必须提前了解业务技术设计,否则接手的时候,很容易忽略数据库层面的业务。
  3. 业务影响:数据库层面做触发器,一旦触发器函数错误,会抛出数据库异常,影响主业务;代码层面可以自主控制,即便业务触发器出错,也不影响主业务。
  4. 数据:数据库层面做触发器,触发器正常运行的情况,数据不会出错或丢失,由于是基于数据库层面实现,手动更改线上数据库表数据,触发器一样会执行,这是业务触发器不能实现的;业务触发器因为一些代码异常或其他异常,会导致触发器业务数据无法正常入库,进而导致数据失真。
10个月前 评论
王小大 10个月前

站长是基佬 (引用文章中片段

10个月前 评论