Laravel. Eloquent. Жадная загрузка

screenshot-laravel com 2016-03-28 10-36-38
Мне так понравился этот раздел доки, что я просто перевёл его.

Как вы знаете, ORM позволяет использовать свойства объекта для обращения к связанным объектам. В этом случае происходит «ленивая загрузка». Это значит, что данные связанного объекта не загружаются до тех пор, пока не будет попытка обратиться к нему.

Eloquent поддерживает «жадную загрузку» («eager load») связей в момент, когда вы получили родительский объект. Это помогает облегчить проблему «N + 1 запроса».

Представим себе модель Book которая привязана к Author:

<?php 
namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * Get the author that wrote the book.
     */
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

А теперь попробуем вытащить все книги вместе с авторами.

$books = App\Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

Этот код выполнит 1 запрос, чтобы получить все книги, а потом для каждой(!) книги будет выполнен 1 запрос, чтобы получить автора. То есть, если мы имеем 25 книг, мы выполним 26 запросов!

Но благодаря использованию жадной загружки мы можем делать только 2 запроса. Для этого используется метод with():

$books = App\Book::with('author')->get();

foreach ($books as $book) {
    echo $book->author->name;
}

В этом случае будет выполнено только два запроса:

select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

Жадная загрузка с несколькими связями
————————————
Если к вашей модели привязаны несколько связей, их тоже можно вытащить жадной загрузкой

$books = App\Book::with('author', 'publisher')->get();

Жадная загрузка цепочки связей
——————————————
Для цепочки последовательных связей можно использовать синтаксис с точкой. Например, чтобы вытащить не только авторов книг, а ещё их контакты делаем так:

$books = App\Book::with('author.contacts')->get();

Дополнительные условия
——————————
А что если не надо жадно тащить всё, только избранное? Просто добавьте условие:

$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();

В этом примере Eloquent «жадно загрузит» только те посты пользователей, у которых титул содержит слово «first». Естественно, можно использовать другие методы Query Builder:

$users = App\User::with(['posts' => function ($query) {
    $query->orderBy('created_at', 'desc');
}])->get();

«Ленивая жадная загрузка»
——————————————-
Предыдущие примеры выполняют жадную загрузку за одну команду. А иногда надо разделить на две команды. Например, чтобы сначала выдернуть книги, а потом подумать, а нужно ли нам вытаскивать авторов.

$books = App\Book::all();

if ($someCondition) {
    $books->load('author', 'publisher');
}

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

$books->load(['author' => function ($query) {
    $query->orderBy('published_date', 'asc');
}]);

Ссылки
———————-
Eloquent: Relationships #Eager Loading