Php Laravel-如何避免N+;1在刀片中加载更多结果时出现问题?
我正在建立一个有意见书的网站,这些意见书都有评论。这些评论的结构与reddit的非常相似。以下是一张供参考的图片: 这些评论有很多关系(auth用户是否保存了/向上投票/向下投票了评论,评论是否有子评论,谁创建了评论) 因此,为了避免大量不必要的服务器查询,我急切地加载这些关系Php Laravel-如何避免N+;1在刀片中加载更多结果时出现问题?,php,laravel,Php,Laravel,我正在建立一个有意见书的网站,这些意见书都有评论。这些评论的结构与reddit的非常相似。以下是一张供参考的图片: 这些评论有很多关系(auth用户是否保存了/向上投票/向下投票了评论,评论是否有子评论,谁创建了评论) 因此,为了避免大量不必要的服务器查询,我急切地加载这些关系 $comments = Comment::with(['children','owner','savedComments','votes']) ->where('submi
$comments = Comment::with(['children','owner','savedComments','votes'])
->where('submission_id', $submission->id)
->where('parent_id', NULL)
->orderBy('removed','asc')
->orderBy($sortBy, $direction)
->paginate(200);
然后我在视图中输入这些注释。但是,请注意,在我的查询中,我只加载父注释
对于儿童,我通过注释的children
关系使用foreach
加载他们
Comment.php:
public function children() {
return $this->hasMany('App\Comment','parent_id')->orderBy('total_score', 'desc');
}
@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
注释\u block.blade.php:
public function children() {
return $this->hasMany('App\Comment','parent_id')->orderBy('total_score', 'desc');
}
@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
@foreach($comment->children as$comment)
@如果($loop->depth==10)
@中断
@elseif($loop->iteration==8&&$totalComments>=25)
加载更多回复({{$loop->剩余+1}})
@中断
@否则
@包括('partials.comment\u block')
@恩迪夫
@endforeach
问题在于,尽管父注释关系被急切加载,但每个子注释也会导致查询,因为它们是通过关系加载到视图中的。因此,我的服务器查询总数从一个增加到了数百个
我如何解决这个问题
编辑:
对于那些说我没有n+1问题的人,以下是我在不加载子注释时的查询:
这就是我加载子注释的时候:
我还使用了这个包,它肯定告诉我我有一个n+1问题。似乎没有办法用Laravel在递归中加载嵌套关系,或者至少在这种情况下没有(尝试在多个位置询问,而不仅仅是stackoverflow) 为了解决我的问题,我将以正常方式加载所有父注释及其关系,但只加载子注释作为数据,然后用AJAX调用填充它们。这将只添加一个服务器查询 您的数据库是什么? 如果数据库支持公共表表达式,则可以将递归数据优化为单个查询。 然而,拉威尔并不支持这些。您必须创建原始查询
如果您的DB不支持CTE(公共表表达式),那么您所能做的最好的事情就是分别将每一级递归@Tim Lewis comment加载到@Felix Maxime answer,这很好地涵盖了该用例。我可能错了,但每个子注释都不会导致新的查询,当您使用
comment::with时,查询只是一个(['children',…])
。当您在视图中执行foreach时,所有子注释都已经存在。正如@porloscerrosψ所说,您没有任何n+1问题,他们是肯定的。我在appserviceprovider中记录我的查询。我会为每个子注释获得额外的查询(在我的案例中是155)。但是如果我删除@foreach($comment->children as$comment)阻止并仅加载父注释,我的查询将下降到1。这是查询的样子:@FelixMaxime您能指出您怀疑刀片视图中的哪一行执行额外查询吗?@CaddyDZ所有子注释关系,但不包括父注释关系。添加子注释时,这是我的警报:out o出于好奇,这个问题的答案有帮助吗?这就是我在同一个模型之间实现递归性所遵循的方法,并对QOL进行了一些修改。@Timlews尝试这一方法只会给我一个ErrorException(注意事项)未定义的偏移量:0@TimLewis我确实发现了这一点:但我不理解代码嗯,不确定为什么会这样;这很复杂,但我能够从第一个链接实现解决方案。是的,我不确定第二个链接在做什么;但看起来很奇怪。ajax方法很好,如果你必须做一个ew调用以获得每一级递归,但效率相当高。