Урок 09

Жадная загрузка и проблема N+1

Проблема N+1

Когда ты обращаешься к связи внутри цикла, Eloquent выполняет отдельный запрос на каждую запись:

$posts = Post::all(); // 1 запрос

foreach ($posts as $post) {
    echo $post->user->name; // N запросов
}

Если постов 100 — будет 101 запрос. Это медленно.

Решение: with

Метод with загружает связи заранее, одним дополнительным запросом:

$posts = Post::with('user')->get(); // 2 запроса

foreach ($posts as $post) {
    echo $post->user->name; // без запросов
}

Вложенная загрузка

Post::with('user', 'comments.user')->get();

Или через точку:

Post::with('comments.user')->get();

Загрузка с условием

Post::with(['comments' => function ($query) {
    $query->where('approved', true)->latest();
}])->get();

Подсчёт связанных

Post::withCount('comments')->get();

foreach ($posts as $post) {
    echo $post->comments_count;
}

С условием:

Post::withCount(['comments' => function ($query) {
    $query->where('approved', true);
}])->get();

Суммы и агрегаты

Post::withSum('orders', 'total')->get();
Post::withAvg('ratings', 'score')->get();
Post::withMax('views', 'count')->get();

Ленивая загрузка после получения

$posts = Post::all();
$posts->load('user', 'comments');

Предотвращение ленивой загрузки

В AppServiceProvider:

use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(!app()->isProduction());
}

Теперь при ленивой загрузке в dev-режиме будет исключение.