Связи в Laravel и MySQL
Если вы совсем новичок и не понимаете, что такое связи в базах данных, то вам лучше прочитать руководство по MySQL. Если коротко обозначить тему, то начать необходимо с разъяснения, что такое реляционные базы данных. Само слово Реляционные (relational -англ.) — обозначает связанные. Освоив связи в базах данных, вы очень быстро освоите и то, как и почему существуют связи в Laravel.
На изображении связи обозначены словом «Код» и символом ключа. В программировании ключи принято задавать в виде ID (идентификатора). Для каждой таблицы существует свой ID, он так и обозначается. Для связанных таблиц, наиболее распространенным обозначением является название_связи_id
.
Связи помогают сохранять целостность данных. Как мы знаем, в базах данных информация хранится атомарно, то есть все, что можно разделить, храниться отдельно. Мухи и котлеты совершенно точно не попадут в одну запись, если вы сами этого не захотите. Но в связи с этим возникает другая проблема. Предположим, мы решили удалить некоего пользователя из системы. Пользователь уже успел создать страницу в нашей соц. сети, добавить сообщения в блог и купить набор коллекционных матрешек. Если мы введем команду DELETE, MySQL удалит пользователя оставив кучу мусора который неизвестно как убирать. Используя связи, мы можем задать правила по которым будут определяться зависимые таблицы. При наличии связей, удаление пользователя скорее всего вызовет ошибку, если он имеет связи.
Но это работает и в другую сторону. К примеру, вы не сможете сделать заказ, если не будет указан пользователь. Связывание подразумевает заполнение всех обязательных ячеек каждой из связанных таблиц.
Отношения один к одному (One to One Relationship)
Описание: Две модели Owner (Владелец) и Car (Автомобиль) соответствующие двум таблицам owners и cars.
Бизнес-логика: у владельца может быть один автомобиль, у автомобиля только один владелец.
Диаграмма отношений:
Детали: Cars должны хранить идентификатор Owner ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Owner { public function car() { return $this->hasOne(Car::class); } } class Car { public function owner() { return $this->belongsTo(Owner::class); } } |
Миграции:
1 2 3 4 5 6 7 8 9 10 11 |
Schema::create('owners', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('cars', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->integer('owner_id')->unsigned()->index()->nullable(); $table->foreign('owner_id')->references('id')->on('owners'); }); |
Сохранение записей:
1 2 3 4 |
// Создание отношений между Owner и Car. $owner->car()->save($car); // Создание отношений между Car и Owner. $car->owner()->associate($owner)->save(); |
Извлечение записей:
1 2 3 4 |
// Получение автомобиля владельца $owner->car; // Получение владельца автомобиля $car->owner; |
Отношения один ко многим (One to Many Relationship)
Описание: две модели Thief (Вор) и Car (Автомобиль) соответствующие двум таблицам thieves и cars
Бизнес-логика: вор может угнать несколько автомобилей, автомобиль может быть угнан одним вором
Диаграмма отношений:
Детали: таблица автомобилей должна хранить идентификатор вора
Модели Eloquent:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Thief { public function cars() { return $this->hasMany(Car::class); } } class Car { public function thief() { return $this->belongsTo(Thief::class); } } |
Миграции:
1 2 3 4 5 6 7 8 9 10 11 |
Schema::create('thieves', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('cars', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->integer('thief_id')->unsigned()->index()->nullable(); $table->foreign('thief_id')->references('id')->on('thieves'); }); |
Сохранение записей:
1 2 3 4 5 6 7 8 9 |
// Создание отношений между Thief и Car. $thief->cars()->saveMany([ $car1, $car2, ]); // или используем метод save() $thief->cars()->save($car); // Создание отношений между Car и Thief. $car->thief()->associate($thief)->save(); |
Извлечение записей:
1 2 3 4 |
// Получение автомобилей вора $thief->cars; // Получение вора автомобиля $car->thief; |
Полиморфные отношения один ко многим (Polymorphic One to Many Relationship)
Описание: три модели Мan (Мужчина), Woman (Женщина) и Car (Автомобиль), соответствующие таблицам men, women и cars
Бизнес-логика: мужчина (buyer) может купить несколько автомобилей, женщина (buyer) может купить несколько автомобилей, автомобиль может быть приобретен одним покупателем (мужчиной или женщиной)
Диаграмма отношений:
Детали: автомобиль должен хранить идентификатор покупателя (Buyer ID) и вид покупателя (Buyer Type). «Buyer» — это условное название группы моделей (Man и Woman). Вообще, количество моделей в ней не ограничено двумя. Buyer Type — это название модели.
Модели Eloquent:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Man { public function cars() { return $this->morphMany(Car::class, 'buyer'); } } class Woman { public function cars() { return $this->morphMany(Car::class, 'buyer'); } } class Car { public function buyer() { return $this->morphTo(); } } |
Миграции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Schema::create('men', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('women', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('cars', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->integer('buyer_id')->unsigned()->index()->nullable(); $table->string('buyer_type')->nullable(); }); |
Сохранение записей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Создание отношений между покупателем (Man/Woman) и Car. $man->cars()->saveMany([ $car1, $car2, ]); $woman->cars()->saveMany([ $car1, $car2, ]); // Или используйте метод save() $man->cars()->save($car); $woman->cars()->save($car); // Создание отношений между Car и buyer (Men/Women). $car1->buyer()->associate($man)->save(); $car2->buyer()->associate($woman)->save(); |
Извлечение записей:
1 2 3 4 5 6 |
// Получение автомобилей покупателя $men->cars $women->cars // Получение покупателя автомобиля $car->buyer |
Отношения многие ко многим (Many to Many Relationship)
Описание: две модели Driver (Водитель) и Car (Автомобиль), три таблицы drivers, cars и сводная (pivot) таблица car_driver
Бизнес-логика: водитель может водить несколько автомобилей, данный автомобиль умеют водить несколько водителей.
Диаграмма отношений:
Детали: сводная таблица car_driver должна хранить идентификаторы водителя и автомобиля (Driver ID, Car ID)
Модели Eloquent:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Driver { public function cars() { return $this->belongsToMany(Car::class); } } class Car { public function drivers() { return $this->belongsToMany(Driver::class); } } |
Миграции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Schema::create('drivers', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('cars', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('car_driver', function (Blueprint $table) { $table->increments('id'); $table->integer('car_id')->unsigned()->index(); $table->foreign('car_id')->references('id')->on('cars')->onDelete('cascade'); $table->integer('driver_id')->unsigned()->index(); $table->foreign('driver_id')->references('id')->on('drivers')->onDelete('cascade'); }); |
Сохранение записей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Cоздание отношений Driver и Car. $driver->cars()->attach([ $car1->id, $car2->id, ]); // или используем метод sync() для защиты от дублирования отношений $driver->cars()->sync([ $car1->id, $car2->id, ]); // Создание отношений Car и Driver. $car->drivers()->attach([ $driver1->id, $driver2->id, ]); // или используем метод sync() для защиты от дублирования отношений $car->drivers()->sync([ $driver1->id, $driver2->id, ]); |
Извлечение записей:
1 2 3 4 5 |
// Получение автомобилей водителя $driver->cars // Получение водителей автомобиля $car->drivers |
Полиморфные отношения многие ко многим (Polymorphic Many to Many Relationship)
Описание: три модели Valet (Работник), Owner (Владелец) и Car (Автомобиль), четыре таблицы valets, owners, cars и drivers (сводная (pivot) таблица).
Бизнес-логика: работник может водить несколько автомобилей, владелец может водить несколько автомобилей, автомобиль может иметь несколько водителей (работник или/и владелец)
Диаграмма отношений:
Детали: сводная (pivot) таблица «drivers» должна хранить идентификатор водителя (Driver ID), тип водителя (Driver Type) и идентификатор автомобиля (Car ID). «driver (водитель)» — это название группы моделей (Valet и Owner). Количество моделей в этой группы не ограничено двумя. Тип водителя — это название модели.
Модели Eloquent:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Valet { public function cars() { return $this->morphToMany(Car::class, 'driver'); } } class Owner { public function cars() { return $this->morphToMany(Car::class, 'driver'); } } class Car { public function valets() { return $this->morphedByMany(Valet::class, 'driver'); } public function owners() { return $this->morphedByMany(Owner::class, 'driver'); } } |
Миграции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Schema::create('valets', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('owners', function (Blueprint $table) { $table->increments('id'); $table->string('name'); }); Schema::create('drivers', function (Blueprint $table) { $table->increments('id'); $table->integer('driver_id')->unsigned()->index(); $table->string('driver_type'); $table->integer('car_id')->unsigned()->index(); $table->foreign('car_id')->references('id')->on('cars')->onDelete('cascade'); }); |
Сохранение записей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// Создание отношений между водителем и автомобилем $valet->cars()->saveMany([$car1, $car2]); $owner->cars()->saveMany([$car1, $car2]); // или используем метод save() $valet->cars()->save($car1); $owner->cars()->save($car1); // Создание отношений между автомобилем и водителем $car->valets()->attach([ $valet1->id, $valet2->id, ]); $car->owners()->attach([ $owner1->id, $owner2->id, ]); // или используем метод sync() для защиты от дублирования отношений $car->valets()->sync([ $valet1->id, $valet2->id, ]); $car->owners()->sync([ $owner1->id, $owner2->id, ]); |
Извлечение записей:
1 2 3 4 5 6 7 |
// Получение автомобилей водителя $valet->cars $owner->cars // Получение владельцев и работников автомобиля $car->owners $car->valets |
Таблица отношений — шпаргалки по отношениям Laravel
Источник для перевода: туть