【发布时间】:2020-01-06 12:36:45
【问题描述】:
我正在建立一个有提交的网站,这些提交有 cmets。这些 cmets 的结构很像 reddit 的结构。这是一张供参考的图片:
这些 cmets 有很多关系(auth 用户是否保存/upvoted/downvoted 评论,评论是否有孩子,谁创建了评论)。
所以为了避免一堆不必要的服务器查询,我渴望加载这些关系。
$comments = Comment::with(['children','owner','savedComments','votes'])
->where('submission_id', $submission->id)
->where('parent_id', NULL)
->orderBy('removed','asc')
->orderBy($sortBy, $direction)
->paginate(200);
然后我在视图中foreach 这些cmets。但是,请注意在我的查询中我只加载父 cmets。
对于儿童,我通过评论的children 关系使用foreach 加载它们。
评论.php:
public function children() {
return $this->hasMany('App\Comment','parent_id')->orderBy('total_score', 'desc');
}
comment_block.blade.php:
@foreach ($comment->children as $comment)
@if ($loop->depth == 10)
<div>
<a href="{{ route('get.submission', ['subchan' => $submission->subchan, 'id' => $submission->id, 'URLtitle' => $submission->URL_title,'commentID' => $comment->parent_id]) }}">Continue this thread</a>
</div>
@break
@elseif ($loop->iteration == 8 && $totalComments >= 25)
<div class="loadMoreReplies"
data-submission-id="{{ $submission->id }}"
data-parent-id="{{$comment->parent_id}}"
>Load More Replies (<span id="remaining-reply-count-{{$comment->parent_id}}">{{ $loop->remaining + 1 }}</span>)</div>
@break
@else
<div class="comment-container comment-container-child" id="comment-container-{{$comment->id}}">
@include('partials.comment_block')
</div>
@endif
@endforeach
这样做的问题是,尽管父评论关系被急切加载,但每个子评论也会引起查询,因为它们是通过关系加载到视图中的。因此,我的服务器查询总数从一个增加到可能数百个。
我该如何解决这个问题?
编辑:
对于那些说我没有 n+1 问题的人,这些是我不加载子 cmets 时的查询:
我也使用laravel query detector 包,它肯定告诉我我有一个 n+1 问题。
【问题讨论】:
-
我可能是错的,但是每个子评论都不会引起新的查询,查询只是一个,当您执行
Comment::with(['children',...])时。当您在视图中执行 foreach 时,所有子 cmets 都已存在。 -
正如@porloscerrosΨ 所说,您没有任何 n+1 问题
-
他们是肯定的。我在 appserviceprovider 中记录我的查询。对于每个子评论,我都会得到一个额外的查询(在我的例子中是 155)。但是,如果我删除 @foreach ($comment->children as $comment) 块并仅加载父 cmets,我的查询将下降到 1。这就是查询的样子:i.imgur.com/Wvz6Gu3.png
-
@FelixMaxime 你能指出你怀疑刀片视图中的哪一行会执行额外的查询吗?
-
@CaddyDZ 所有子评论关系,但不包括父评论关系。这是我添加子 cmets 时的提醒:i.imgur.com/5INCTqp.png