Laravel 4.2. Жадная загрузка в Eloquent ORM (Eager Loading)

Категория: Laravel

«Жадная» загрузка позволяет предварительно загрузить связанные с коллекцией моделей сущностей и упаковать их как вложенная коллекция для каждой модели. Данный подход позволяет избавиться от проблемы N+1. Например, мы получили коллекцию $users и хотим показать заголовки постов каждого юзера:

$users = User::all();
foreach($users as $user) {
  echo $user->posts->title;
}

В этом случае на каждой итерации будет выполнен запрос на получение постов юзера:

SELECT * from users
SELECT * from posts WHERE user_id = 1
SELECT * from posts WHERE user_id = 2
SELECT * from posts WHERE user_id = N
...

Т.е. для сотни юзеров мы выполним 100+1 запросов. Не эффективно! Особенно если БД находится на другом сервере.

«Жадная» загрузка позволяет выполнить всего 2 запроса - один для получения юзеров, а второй для загрузки всех связанных постов:

SELECT * FROM users
SELECT * FROM posts WHERE user_id IN (1, 2, N, ...)

Для моделей, которым нужно предварительно "вложить" (загрузить) связанные сущности, необходимо описать тип связи одним из методов: BelongsToMany, BelongsTo, HasMany, HasManyThrough, HasOne.

«Жадная» загрузка данных выполняется методом with():

User::with('posts')->get();

User::with('posts', 'posts.comments')->get();

$users = User::with(['posts' => function($q) {
  $q->where('title', 'like', '%php%')
    ->orderBy('created_at', 'desc');
}])->get();

Возможно, вы уже поняли доступный функционал по примерам, но я подытожу возможности «жадной» загрузки.

Eloquent может:

  • Подгружать несколько связанных моделей/коллекций;
  • Использовать при подгрузке зависимые/дочерние связи (posts.comments);
  • Применять фильтрацию и сортировку для загружаемых записей.

Также мы можем загрузить связанные модели для уже полученной коллекции:

$users->load('posts', 'posts.comments');
Внимание!

Будьте внимательны при вызове with() для моделей, в связях с которыми используются свойства текущей модели: $this->property! При вызове «жадной» загрузки через with() свойства модели(ей) еще не содержат данных! В таких случаях можно использовать только load() - ленивую загрузку.

Примечание

Если в качестве связующих полей используется не PK, а, допустим, текстовое поле email - то Eloquent сформирует запрос наподобие:

SELECT * from users
SELECT * FROM posts WHERE email IN ('mail_1@io', 'mail_2@io', ...)

#eloquent, #eager loadind, #lazy loading, #relationship

категория: Laravel