要么改变世界,要么适应世界

二级评论展示的实现方法

2021-03-02 20:09:07
352
目录

前言

通常的博客评论展示有一级评论、二级评论、多级评论,不同的展示方法给用户的体验不一样。

一级评论

顾名思义,就是所有评论(回复)是同级关系,这样子是设计简单,查询迅速,表可以设计成:

字段 字段说明
id 主键
blog_id 所属博客的id
content 评论内容

查询某一篇文章的评论时候可以使用

SELECT * FROM t_comment WHERE blog_id = 99;

是不是非常的简单!

但是这样子用户体验就太差了,对于回复的评论,用户根本无法建立起回复的桥梁。

多级评论

简单来说就是层层嵌套,每一条评论具有严格的父子关系。表格可以设计成:

字段 字段说明
id 主键
blog_id 所属博客的id
content 评论内容
parent_comment_id 父级评论id,顶层评论为-1

JavaBean设计:

public class Comment {
    private Long id;
	private String content;
    private Long blogId;
    private Long parentCommentId;
    private List<Comment> replyComments;
    // 省略set和get方法
}

查询的时候,对于每一条顶层评论,拿到顶层评论的id后,将该id作为参数,可以查出二级评论,将二级评论的id作为参数,可以查出三级评论......

借助Mybatis,查询语句可以写成:

<mapper namespace="top.yalexin.rblog.mapper.CommentMapper">
    <select id="findTopCommentsByParentIdAndBlogId" resultMap="topComment">
        select * from t_comment as cmt where cmt.parent_comment_id=#{parentId} and cmt.blog_id=#{blogId} 
    </select>
    <resultMap id="topComment" type="Comment">
        <id column="id" property="id"></id>
        <result property="content" column="content"/>
        <result property="blogId" column="blog_id"/>
        <result property="parentCommentId" column="parent_comment_id"/>
        <!-- 这一步使得JDBC递归进行查询 -->
        <association property="replyComments" select="findTopCommentsByParentIdAndBlogId"
                     column="{blogId=blog_id,parentId=id}"></association>
    </resultMap>
</mapper>

查询结果类似于:

[
	{
        id: 1,
        content: 'hello',
        parent_comment_id: -1,
        replyComments: [
            {
                id: 2,
                content: 'hello',
                parent_comment_id: 1,
                replyComments:[
                    {
                        id: 3,
                        content: 'hello',
                        parent_comment_id: 2,
                        replyComments:[]
                    }
                ]
            }
        ]
    },
    {
        id: 4,
        content: 'hello',
        parent_comment_id: -1,
        replyComments: []
    }
]

类似的效果:

虽然看上去层级关系清晰明了,但是当回复树深度很大的时候,页面将变得十分难看,所以该方式也很少使用。

二级评论

该方式先把每一个顶级评论展示,然后每一个顶级评论下的所有评论都处理成二级评论

该方式查询过程也很简单,查询语句和多级评论的方式一样,需要改变的是查询结束后的处理:

如果将每一个顶级评论和该评论下的子孙评论看成一棵树的话,为每一个顶级评论创建一个replyCmts容器,那么我们可以递归进行,遇到叶节点时,将叶节点的父节点的replyComments设为空,同时将叶节点添加到replyCmts容器中,此时先前的父节点又变成了叶节点,此时就可以递归返回,直至处理完所有节点,下面是代码实现:

@Override
public List<Comment> getTopCommentsByBlogId(Long blogId) {
    if (blogId == null || blogId < 0) return null;
    List<Comment> rawCmts = commentMapper.findTopCommentsByParentIdAndBlogId((long) -1, blogId);
    return getParent(rawCmts);
}

private List<Comment> getParent(List<Comment> rawComments) {
    // 对于每一个顶级回复
    for (Comment topComment : rawComments) {
        // 将顶级评论的子孙评论归结到一个集合中
        LinkedList<Comment> comments = new LinkedList<>();
        List<Comment> replyCmtsByTopCmt = topComment.getReplyComments();
        for (Comment replyComment : replyCmtsByTopCmt) {
            handleChild(replyComment, comments);
//                replyComment.setReplyNickname(topComment.getId().toString());
            replyComment.setReplyNickname(topComment.getNickname());
        }
        topComment.setReplyComments(comments);
    }
    return rawComments;
}

//处理二级评论以及子评论
private void handleChild(Comment replyComment, List<Comment> parent) {
    List<Comment> grandchildren = replyComment.getReplyComments();
    replyComment.setReplyComments(null);
    parent.add(replyComment);
    for (Comment grandChild : grandchildren) {
//            grandChild.setReplyNickname(replyComment.getId().toString());
        grandChild.setReplyNickname(replyComment.getNickname());
        if (grandChild.getReplyComments() != null) handleChild(grandChild, parent);
    }
}

查询结果:

[
	{
        id: 1,
        content: 'hello',
        parent_comment_id: -1,
        replyComments: [
            {
                id: 2,
                content: 'hello',
                parent_comment_id: 1,
                replyComments:[]
            },
            {
                id: 3,
                content: 'hello',
                parent_comment_id: 2,
                replyComments:[]
			}
        ]
    },
    {
        id: 4,
        content: 'hello',
        parent_comment_id: -1,
        replyComments: []
    }
]

该方式结合了一级评论和多级评论的优点,深得广大开发者的热爱。

历史评论
开始评论