Laravel 4.2. Жадная загрузка в Eloquent ORM (Eager Loading)
«Жадная» загрузка позволяет предварительно загрузить связанные с коллекцией моделей сущностей и упаковать их как вложенная коллекция для каждой модели. Данный подход позволяет избавиться от проблемы 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