Как прописать отношения между моделями в Laravel Eloquent?

«Как прописать отношения между моделями в Laravel Eloquent?» — вопрос из категории Laravel, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Laravel Eloquent отношения определяются как методы в классе модели. Они строят SQL-запросы для связывания данных.

Основные типы отношений:

1. Один к одному (hasOne / belongsTo) Предположим, у User есть один Phone.

// В модели User
public function phone() {
    // User имеет один Phone. Второй аргумент — foreign key в таблице phones
    return $this->hasOne(Phone::class, 'user_id');
}

// В модели Phone
public function user() {
    // Phone принадлежит User. Второй аргумент — foreign key в таблице phones
    return $this->belongsTo(User::class, 'user_id');
}

// Использование:
$phone = User::find(1)->phone; // Получить телефон пользователя
$user = $phone->user; // Получить владельца телефона

2. Один ко многим (hasMany / belongsTo) У Post есть много Comment.

// В модели Post
public function comments() {
    return $this->hasMany(Comment::class);
}

// В модели Comment
public function post() {
    return $this->belongsTo(Post::class);
}

// Использование с жадной загрузкой (Eager Loading) для N+1 проблемы:
$posts = Post::with('comments')->get();
foreach ($posts as $post) {
    foreach ($post->comments as $comment) { // Доп. запросов не будет
        // ...
    }
}

3. Многие ко многим (belongsToMany) User может иметь много Role, и наоборот. Требуется промежуточная таблица (например, role_user).

// В модели User
public function roles() {
    return $this->belongsToMany(Role::class); // Ищет таблицу 'role_user'
}

// Использование:
$user = User::find(1);
foreach ($user->roles as $role) {
    echo $role->name;
}
// Присоединить роль к пользователю
$user->roles()->attach($roleId);
$user->roles()->sync([1, 2, 3]); // Синхронизировать список ролей

4. Полиморфные отношения Позволяют модели принадлежать более чем одному типу модели. Например, Comment может принадлежать и Post, и Video.

// В модели Comment
public function commentable() {
    return $this->morphTo();
}

// В моделях Post и Video
public function comments() {
    return $this->morphMany(Comment::class, 'commentable');
}

// Использование:
$comment = Comment::find(1);
$commentable = $comment->commentable; // Получит либо Post, либо Video

Ключевые моменты:

  • Имена методов — это соглашение, но можно переопределить имена таблиц и ключей.
  • Всегда используйте жадную загрузку (with()) при обращении к отношениям в циклах, чтобы избежать проблемы N+1 запроса.
  • Отношения — это построители запросов (Query Builders), к ним можно добавлять условия: $user->posts()->where('active', 1)->get().