菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
3375
17

3 条 sql 是实现知乎评论,7 条 sql 实现点赞 + 评论,且可扩展

原创
05/13 14:22
阅读数 2345

数据结构

文章数据结构

Post
    - id
    - title
    - body
    - vote_count
    - comment_count

评论数据结构

Comment
    - id
    - title
    - body
    - commentable_id
    - commentable_type
    - parent_id
    - user_id
    - first_depth_id 一级评论id
    - vote_count
    - comment_count

点赞数据结构

    Vote
        - id
        - user_id
        - voteable_id
        - voteable_type

Model

Post

public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }

    public function votes()
    {
        return $this->morphMany(Vote::class, 'voteable');
    }

Comment

 public function commentable()
    {
        return $this->morphTo();
    }

    public function parent()
    {
        return $this->belongsTo(Comment::class,'parent_id');
    }

    public function children()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function votes()
    {
        return $this->morphMany('App\Vote', 'voteable');
    }

    public function seconds()
    {
        return $this->hasMany(Comment::class,'first_depth_id');
    }

Vote

    public function voteable()
    {
        return $this->morphTo();
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

查询评论

1直接用模型查询评论及用户的数据,数据类型也符合前端的要求

直接用sql去查文章的评论及用户(不建议),因为用了7条sql

 $posts = Post::with(['comments'=>function ($query){
        $query->where('parent_id',null)->with(['user','seconds'=>function($query){
            $query->with('user','parent.user');
        }]);
    }])->get();

debug 显示
file
返回到前端的数据
file

2查询出所有评论及用户,再去解析数据,返回前端。

该查询用到三条sql(建议)

$posts = Post::with('comments','comments.user')->get();
$posts = collect($posts->toArray())->map(function ($post){
    $nestedKeys = [];
    $post['comments'] = array_column($post['comments'], null, 'id');
    foreach ($post['comments'] as $key=>$comment){
        if(!$parent_id=$comment['parent_id']){
            continue;
        }
        if (array_key_exists($first_depth_id=$comment['first_depth_id'], $post['comments'])) {
            if(!isset($post['comments'][$first_depth_id]['seconds'])){
                $post['comments'][$first_depth_id]['seconds'] = [];
            }
            $comment['parent']=$post['comments'][$parent_id];
            $post['comments'][$first_depth_id]['seconds'][] = $comment;
            $nestedKeys[]=$key;
        }else{
            $nestedKeys[]=$key;
        }
    }
    foreach ($nestedKeys as $val){
        unset($post['comments'][$val]);
    }
    return $post;
});

debug
file
返回到前端的数据,可以看到和上方的返回数据是一样的,而这个只用了三条sql
file

查询点赞+评论

由于点赞没有无限极的影响,所以实现起来比较简单

直接用模型实现

在评论的基础上加上点赞和点赞的用户即可(不建议,因为sql增加到12条)

$posts = Post::with(['comments'=>function ($query){
            $query->where('parent_id',null)->with(['votes'=>function($query){
                $query->with('user');
            },'user','seconds'=>function($query){
                $query->with(['user','parent.user','votes'=>function($query){
                    $query->with('user');
                }]);
            }]);
        },'votes'=>function($query){
            $query->with('user');
        }])->get();

debug
file
返回的数据
file

先查出总数据再去解析

在评论的基础上加个点赞即可,用了7条sql(建议)

$posts = Post::with('comments','comments.user','votes.user','comments.votes.user')->get();
$posts = collect($posts->toArray())->map(function ($post){
    $nestedKeys = [];
    $post['comments'] = array_column($post['comments'], null, 'id');
    foreach ($post['comments'] as $key=>$comment){
        if(!$parent_id=$comment['parent_id']){
            continue;
        }
        if (array_key_exists($first_depth_id=$comment['first_depth_id'], $post['comments'])) {
            if(!isset($post['comments'][$first_depth_id]['seconds'])){
                $post['comments'][$first_depth_id]['seconds'] = [];
            }
            $comment['parent']=$post['comments'][$parent_id];
            $post['comments'][$first_depth_id]['seconds'][] = $comment;
            $nestedKeys[]=$key;
        }else{
            $nestedKeys[]=$key;
        }
    }
    foreach ($nestedKeys as $val){
        unset($post['comments'][$val]);
    }
    return $post;
});

debug 增加的4条sql是不可避免的
file
返回的数据
file

总结

一般来说查询sql的时间,要多于程序解析的时间,所以把需要用到的数据,查询出来,再去由程序去解析成相应的数据类型,而不要用sql去实现。
【2019/8/8 更新】
上面的总结过于粗暴~ 数据量大的话,要用到其他优化方式,比如加缓存。具体业务,在具体分析,这里抛砖引玉

发表评论

0/200
3375 点赞
17 评论
收藏
为你推荐 换一批