给帖子发表回复后更新帖子回复总数的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 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

所以这几种性能如何呢~

1周前 评论

总结呢 :see_no_evil:

1周前 评论
sanders 1周前
curry丶 (作者) 1周前
陈先生

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

1周前 评论

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

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

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!