Связи между моделями
Таблицы в базе связаны между собой: у статьи есть автор и комментарии. В 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();
}