Laravel - 我的关系递归中的 N+1 问题

Fel*_*ime 9 php laravel

我有一个正在构建的 Laravel 网站。它有提交,提交有评论。我想急切加载这些评论及其子评论(以及这些评论的子评论等)。但仅限于一点。

子评论循环只加载$loop->depth10以内的子评论,以及$loop->iteration8以内的子评论。我不想急切加载我什至不会显示的评论。

这是我到目前为止:

控制器:

$repliesCount = Comment::with([
            'owner',
            'savedComments',
            'votes',
        ])
        ->where('submission_id', $submission->id)
        ->whereNotNull('parent_id')
        ->count();


$comments = Comment::where('submission_id', $submission->id)
    ->whereNull('parent_id')
    ->with([
        'owner',
        'savedComments',
        'votes',
        'submission',
        'reports'
    ])
    ->orderBy('removed', 'asc')
    ->orderBy($sortBy, $direction)
    ->paginate(100);

$replies = Comment::where('submission_id', $submission->id)
            ->whereNotNull('parent_id')
            ->with([
                'owner',
                'savedComments',
                'votes',
                'submission',
                'reports'
            ])
            ->orderBy('removed', 'asc')
            ->orderBy($sortBy, $direction)
            ->paginate($repliesCount);
            
$comments_by_id = new Collection();
$replies_by_id = new Collection();
foreach ($comments as $comment) {
    $comments_by_id->put($comment->id, $comment);
    $comments_by_id->get($comment->id)->children = new Collection();
}
foreach ($replies as $reply) {
    $replies_by_id->put($reply->id, $reply);
    $replies_by_id->get($reply->id)->children = new Collection();
}
foreach ($replies as $key => $reply) {
    if ($comments_by_id->get($reply->parent_id)) {
        $comments_by_id->get($reply->parent_id)->children->push($reply);
    } elseif ($replies_by_id->get($reply->parent_id)) {
        $replies_by_id->get($reply->parent_id)->children->push($reply);
    }
}
Run Code Online (Sandbox Code Playgroud)

刀刃:

@foreach ($comment->children as $comment)
    @if ($loop->depth == 10)
        <div>
            <a href="{{ route('get.submission', ['subchan' => $comment->submission->subchan, 'id' => $comment->submission->id, 'URLtitle' => $comment->submission->URL_title,'commentID' => $comment->parent_id]) }}">Continue this thread</a>
        </div>
        @break
    @endif
    <div class="comment-container comment-container-child" id="comment-container-{{$comment->id}}" hidden-level="{{ceil(($loop->iteration + 3) / 10) - 1}}">
        @include('partials.comment_block')
    </div>
    @if ($loop->iteration % 10 == 7 && $loop -> remaining > 0)
        
        <div class="loadMoreReplies comment-container-child load-more"
        hidden-level="{{ceil(($loop->iteration + 3) / 10) - 1}}"
        data-submission-id="{{ $comment->submission->id }}"
        data-parent-id="{{$comment->parent_id}}"
        
        >Load More Replies (<span id="remaining-reply-count-{{$comment->parent_id}}">{{ $loop->remaining }}</span>)</div>
    @endif
@endforeach
Run Code Online (Sandbox Code Playgroud)

基本上,我接受了 100 条评论 ( $comments),并且我急切地加载了我需要的关系(例如“所有者”关系)。

然后我进行查询以获取该提交 ( $replies) 及其所有关系的所有儿童评论。

之后,我创建了一个集合,将每个孩子的回复推送到其父级comment->children

这有效。但是,它会加载给定评论的所有子级回复。因此,即使我只加载 100 条父评论,如果其中一条评论或子评论有数千条子评论,它也会一次性加载所有评论。

这是一张可以更好地说明的图像(5000 个回复被隐藏,但仍在被查询,因此页面加载需要很长时间):

在此处输入图片说明

我最终想要的是它只急切加载并返回最多 10 个子评论,同时还传递评论有多少孩子,以便我可以显示有多少孩子失踪。

TEF*_*EFO 2

它有点复杂。您需要两个子关系,一个仅获取 10 条记录,另一个获取所有记录(默认情况下)。所以我假设您渴望通过 $ 等属性加载模型中的所有注释$with = ['children'];。相反,您可以在评论模型中执行此操作:

class Comment extends Model
{
    protected $with = ['tenChildren'];

    public function tenChildren()
    {
        // fill others params if needed
        // sort it or whatever
        return $this->hasMany(Comment::class)->limit(10);
    }

    public function children()
    {
        return $this->hasMany(Comment::class);// fill others params if needed
    }
}
Run Code Online (Sandbox Code Playgroud)

所以第一次只有带10个孩子的家长收到。那么你只需要从后端获取剩下的孩子,在这里你有一点问题。使用具有状态(以检测收到了多少个孩子)的 js 现代框架,这变得很容易。但要获得额外的子级,只需要一条在分页数据中获取子级的路线。对于前要让其他孩子发表第二条评论,您需要将 get req 发送到此 add /api/comments/2?page=2