Глобальное предотвращение проблемы N+1 в Laravel с автоматической жадной загрузки
Проблема N+1 может стать проблемой производительности, если много связанных моделей. Жадная загрузка позволяет загрузить все связанные модели в одном запросе, что может значительно повысить производительность.
Например, если есть модель User
, содержащая множество моделей Post
, то можно выполнить жадную загрузку сообщений следующим образом:
$users = User::with('posts')->get();
foreach ($users as $user) {
// Это не приведёт к выполнению отдельного запроса для каждого пользователя
echo $user->posts->title;
}
В данном случае загрузятся все пользователи и их сообщения в одном запросе, вместо загрузки сообщения каждого пользователя по одному.
$users = User::all();
foreach ($users as $user) {
// Это приведёт к выполнению отдельного запроса для каждого пользователя
echo $user->posts->title;
}
В этом случае возникнет проблема N+1, когда один запрос загружает всех пользователей, а затем один запрос для каждого пользователя загружает его сообщение.
Это замечательно, но, как вы можете заметить, жадная загрузка не всегда является поведением по умолчанию в Laravel. Необходимо явно указать Laravel на жадную загрузку связанных моделей с помощью метода with()
.
Для исправления этой ситуации в Laravel появилась возможность глобально включить жадную загрузку, автоматически загружающую связанные модели.
Глобальная автоматическая жадная загрузка
В Laravel 12.x появился новый метод automaticallyEagerLoadRelationships()
для класса Model
. Этот метод позволяет включить автоматическую жадную загрузку всех моделей в приложении.
Включить эту функцию можно, добавив следующий код в AppServiceProvider
:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Model::automaticallyEagerLoadRelationships();
}
}
Таким образом, теперь даже код с проблемой N+1, продемонстрированный ранее, будет работать без каких-либо проблем.
// Поскольку автоматическая жадная загрузка включена глобально,
// этот код будет работать без возникновения проблемы с N+1
$users = User::all();
foreach ($users as $user) {
echo $user->posts->title;
}
Автоматическая жадная загрузка моделей
Если не хотите включать автоматическую жадную загрузку глобально, можно включить её для отдельной модели. Для этого необходимо добавить в модель метод automaticallyEagerLoadRelationships()
следующим образом.
use App\Models\User;
User::automaticallyEagerLoadRelationships();
Автоматическая жадная загрузка запросов
И наконец, можно включить автоматическую жадную загрузку на основе отдельного запроса. Для этого необходимо использовать метод withRelationshipAutoloading()
, как показано ниже.
$users = User::all()->withRelationshipAutoloading();
foreach ($users as $user) {
echo $user->posts->title;
}
Заключение
Я думаю, что это отличная функциональная возможность в Laravel и потенциальное спасение для новичков, не знакомых с жадной загрузкой.
Подробнее об этой функции вы можете прочитать в PR: [12.x] Added Automatic Relation Loading (Eager Loading) Feature #53655