Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Связи между моделями

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

Внешний ключ

Связь хранится в столбце с идентификатором другой таблицы. В миграции комментариев его создаёт одна строка:

Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->text('body');
    $table->timestamps();
});

cascadeOnDelete() удалит комментарии вместе со статьёй.

Один ко многим

У статьи много комментариев — метод hasMany():

class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Связанные записи читают как свойство, без скобок:

$post = Post::find(1);
 
foreach ($post->comments as $comment) {
    echo $comment->body;
}

Обратная связь

Комментарий принадлежит одной статье — метод belongsTo():

class Comment extends Model
{
    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}
echo $comment->post->title;

Создание связанной записи

Через связь запись создаётся сразу с нужным post_id:

$post->comments()->create(['body' => 'Отличная статья!']);

Здесь связь вызвана как метод, со скобками: так получают построитель запросов, а не готовый список.

Многие ко многим

У статьи много тегов, а у тега много статей. Такой связи нужна промежуточная таблица post_tag со столбцами post_id и tag_id:

class Post extends Model
{
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}
$post->tags()->attach($tagId);  // привязать тег
$post->tags()->detach($tagId);  // отвязать тег

Жадная загрузка

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

$posts = Post::with('comments')->get();

Частая ошибка

Без with() обращение к связи в цикле выполняет отдельный запрос для каждой записи. Для ста статей получится сто один запрос — это называют проблемой N+1.

// Медленно: запрос комментариев на каждой итерации.
foreach (Post::all() as $post) {
    echo $post->comments->count();
}
 
// Быстро: всего два запроса.
foreach (Post::with('comments')->get() as $post) {
    echo $post->comments->count();
}

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.