3 条 sql 是实现知乎评论,7 条 sql 实现点赞 + 评论,且可扩展 
                                                    
                        
                    
                    
  
                    
                    数据结构
文章数据结构
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 显示
返回到前端的数据
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
返回到前端的数据,可以看到和上方的返回数据是一样的,而这个只用了三条sql
查询点赞+评论
由于点赞没有无限极的影响,所以实现起来比较简单
直接用模型实现
在评论的基础上加上点赞和点赞的用户即可(不建议,因为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
返回的数据
先查出总数据再去解析
在评论的基础上加个点赞即可,用了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是不可避免的
返回的数据
总结
一般来说查询sql的时间,要多于程序解析的时间,所以把需要用到的数据,查询出来,再去由程序去解析成相应的数据类型,而不要用sql去实现。
【2019/8/8 更新】
上面的总结过于粗暴~ 数据量大的话,要用到其他优化方式,比如加缓存。具体业务,在具体分析,这里抛砖引玉
本作品采用《CC 协议》,转载必须注明作者和本文链接
          
                    
                    
            
          
          
                关于 LearnKu
              
                    
                    
                    
 
推荐文章: