Связи между моделями
Зачем нужны связи
Таблицы в БД связаны между собой: у поста есть автор, у поста много комментариев, у поста много тегов. Eloquent позволяет описать эти связи в коде и обращаться к связанным данным как к свойствам.
Один ко многим
Ситуация: один пользователь → много постов.
User:
public function posts()
{
return $this->hasMany(Post::class);
}
Post:
public function user()
{
return $this->belongsTo(User::class);
}
Миграция для posts:
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
Использование:
$user = User::find(1);
foreach ($user->posts as $post) {
echo $post->title;
}
$post = Post::find(1);
echo $post->user->name;
Один к одному
Ситуация: у пользователя один профиль.
public function profile()
{
return $this->hasOne(Profile::class);
}
Обратная связь — belongsTo.
Многие ко многим
Ситуация: у поста много тегов, у тега много постов.
public function tags()
{
return $this->belongsToMany(Tag::class);
}
Нужна промежуточная таблица:
Schema::create('post_tag', function (Blueprint $table) {
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['post_id', 'tag_id']);
});
Операции:
$post->tags()->attach($tagId);
$post->tags()->detach($tagId);
$post->tags()->sync([1, 2, 3]);
Полиморфные связи
Ситуация: комментарий может принадлежать и посту, и видео.
public function commentable()
{
return $this->morphTo();
}
Миграция:
$table->morphs('commentable');
Жадная загрузка
Проблема N+1: при обращении к связи внутри цикла выполняется отдельный запрос на каждую запись.
foreach (Post::all() as $post) {
echo $post->user->name; // запрос на каждый пост
}
Решение — предзагрузка через with:
foreach (Post::with('user')->get() as $post) {
echo $post->user->name; // один дополнительный запрос
}
Вложенная загрузка:
Post::with('user', 'comments.user')->get();