手把手教你用gin+gorm+mysql实现多级评论

编程入门 行业动态 更新时间:2024-10-18 20:26:58

<a href=https://www.elefans.com/category/jswz/34/1765412.html style=手把手教你用gin+gorm+mysql实现多级评论"/>

手把手教你用gin+gorm+mysql实现多级评论

文章目录

  • 写在前面
  • 仓库地址
  • 数据库设计
  • 如何使用

写在前面

话不多说,先看多级评论的最后效果:

并且评论可以一直嵌套下去,实现了无限评论与回复。有点类似于抖音app的评论区。

仓库地址

关于多级评论demo,所有代码均放到了GitHub仓库:

有需要者可克隆使用。demo主要包括了三个功能:用户发表动态,发表评论,查看评论。

数据库设计

user表:

type User struct {gorm.ModelNickname string `gorm:"not null;index;varchar(20)"` // 昵称Password string `gorm:"not null"`                   // 密码Avatar   string `gorm:"not null;"`                  // 头像
}

moment表:

type Moment struct {gorm.ModelUserId  int    `gorm:"not null;index"`User    User   `gorm:"foreignKey:UserId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`Content string `gorm:"size:2048"`
}

这里给UserId设置了外键,参照UserID,并设置了级联删除与更新。

comment表:

type Comment struct {gorm.ModelMomentId int      `gorm:"not null;index"`Moment   Moment   `gorm:"foreignKey:MomentId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`UserId   int      `gorm:"not null;index"`User     User     `gorm:"foreignKey:UserId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`ParentId *int     `gorm:"index;default:NULL"`Parent   *Comment `gorm:"foreignKey:ParentId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`Content  string   `gorm:"not null;size:1024"`
}

评论表是比较关键的部分,MomentIdUserIdParentId均设置了外键与级联删除更新,这样做的目的是为了适应一些场景,比如动态被删了,那么评论也会都从表中删掉,父评论删了,那么它对应的子孙评论也会一并删除,这样处理才符合真实的业务逻辑。ParentId顾名思义就是父评论的ID,但由于初始评论没有父评论,所以默认为NULL,这里注意用*int而不用int的原因是:如果用后者,默认空值是0,那么即使创建评论数据时不传入该字段,也会当0处理,这样插入到数据库就会报错,因为ParentId是外键,而最开始的数据库不可能有ID为0的数据,所以就会发生参照外键的错误,导致插入失败,但是不设置外键就没有级联的效果,这样也不符合真实的场景。于是将该字段换位指针类型,这样默认空值为nil,插入到数据库就会显示NULL,这样就能实现插入第一条评论了,可见下图:

关于评论还有一个结构体,用来表示评论树:

type CommentTree struct {CommentId int                    `json:"cid"`Content   string                 `json:"content"`Author    map[string]interface{} `json:"author"`CreatedAt string                 `json:"createdAt"`Children  []*CommentTree         `json:"children"`
}

通过评论树就可以实现与子孙评论的无限嵌套。

如何使用

关于mysql连接初始化,路由配置,表单验证等均在可以在仓库找到相应的代码,这里不做赘述。

下面讲解真实的业务逻辑。

有动态才能被评论,所以我们先发布一条动态。发布动态的代码可以在仓库里找到。

记住动态ID为2,发布评论时会用到。

下面附上发布评论的核心代码:

func AddComment(c *gin.Context) {addCommentForm := form.AddCommentForm{}if err := c.ShouldBind(&addCommentForm); err != nil {c.JSON(http.StatusBadRequest, gin.H{"code": 400,"msg":  err.Error(),})return}comment := model.Comment{UserId:   addCommentForm.UserId,MomentId: addCommentForm.MomentId,Content:  addCommentForm.Content,}if addCommentForm.ParentId != 0 {comment.ParentId = &addCommentForm.ParentId}global.DB.Create(&comment)c.JSON(http.StatusOK, gin.H{"code": 200,"msg":  "success",})
}

如果是第一条评论,也就是父评论,那么前端在传的时候ParentId0就好,因为它不可能有父评论。我的代码通过判断ParentId是0后,就不需要传入该字段,因为在刚才讲解数据库设计的时候,ParentId在数据库中默认为NULL

下面新建一条评论:

为了之后的显示效果,我又多发布了几条评论。

查看评论是也是比较核心的部分:

func GetComments(c *gin.Context) {var commentTrees []model.CommentTreemomentId := c.Query("mid")if momentId == "" {c.JSON(http.StatusBadRequest, gin.H{"code": 400,"msg":  "mid不能为空",})return}mid, _ := strconv.Atoi(momentId)commentTrees = GetMomentComment(mid)c.JSON(http.StatusOK, gin.H{"code": 200,"msg":  "success","data": commentTrees,})
}

GetMomentComment函数比较核心的函数,你只需要传入动态的ID进去,就可以获取到该动态的评论树。该函数会查询动态所有的一级评论,并把该一级评论的所有子孙评论放到它的Children里。同时该函数会调用GetMomentCommentChild函数,这个函数是根据某条父评论,以获取其所有子孙评论,使用了递归。通过这两个函数,便可以得到一株评论树,两个函数的细节可能会有难理解的地方,但对于我们API工程师来说,只要会调用就行😎。

func GetMomentComment(mid int) []model.CommentTree {var commentTrees []model.CommentTreevar comments []model.Commentglobal.DB.Where("moment_id = ? AND parent_id IS NULL", mid).Order("id desc").Find(&comments)for _, comment := range comments {var user model.Usercid := int(comment.ID)uid := comment.UserIdglobal.DB.Where("id = ?", uid).First(&user)commentTree := model.CommentTree{CommentId: cid,Content:   comment.Content,Author:    gin.H{"uid": uid, "nickname": user.Nickname, "avatar": user.Avatar},CreatedAt: comment.CreatedAt.Format("2006-01-02 15:04"),Children:  []*model.CommentTree{},}GetMomentCommentChild(cid, &commentTree)commentTrees = append(commentTrees, commentTree)}return commentTrees
}func GetMomentCommentChild(pid int, commentTree *model.CommentTree) {var comments []model.Commentglobal.DB.Where("parent_id = ?", pid).Find(&comments)// 查询二级及以下的多级评论for i, _ := range comments {var user model.Usercid := int(comments[i].ID)uid := comments[i].UserIdglobal.DB.Where("id = ?", uid).First(&user)child := model.CommentTree{CommentId: cid,Content:   comments[i].Content,Author:    gin.H{"uid": user.ID, "nickname": user.Nickname, "avatar": user.Avatar},CreatedAt: comments[i].CreatedAt.Format("2006-01-02 15:04"),Children:  []*model.CommentTree{},}commentTree.Children = append(commentTree.Children, &child)GetMomentCommentChild(cid, &child)}
}

以下是查看评论的效果:

到这儿,实现多级评论就全部完成了,完整代码均在Github仓库中,欢迎查阅。

更多推荐

手把手教你用gin+gorm+mysql实现多级评论

本文发布于:2023-12-04 11:56:07,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1660835.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:手把手   教你用   gin   gorm   mysql

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!