
Например, если клиент сделал заказ, то уже не получится удалить его из базы данных просто так.
При связывании таблиц отношением один к одному или один ко многим, нужно сначала определить какая из них является родительской - у нее связь делается по существующему первичному ключу (обычно это поле «id») и какая дочерней - в ней нужно будет создать дополнительное поле для связи с родительской таблицей, например «user_id» и внешний ключ для этого поля.
Так, например, пользователь может сделать несколько заказов, но заказы не могут быть без пользователя (покупателя). Тут:
- users – родительская таблица;
- customers – дочерняя таблица.
В данном случае, связывающее поле user_id и внешний ключ для него необходимо создавать в дочерней таблице (customers).
Рассмотрим особенности работы со связанными таблицами в php-фреймворке Laravel.
Пример создания файла миграции в консоли.
Создать пустой файл миграции с названием CreateRoleTables:
php artisan make:migration CreateRoleTables
php artisan make:migration CreateRoleTables --create=roles
Примеры миграций для создания связывающего поля/таблицы и внешних ключей.
Миграция – создание связывающего поля с внешним ключом (связь один к одному и один ко многим).
Миграция для создания таблицы 'countries' со связывающим полем и внешним ключом:
public function up() { Schema::create('countries', function (Blueprint $table) { $table->increments('id'); $table->string('name'); //создание поля для связывания с таблицей user table->integer('user_id')->unsigned()->default(1); //создание внешнего ключа для поля 'user_id', который связан с полем id таблицы 'users' $table->foreign('user_id')->references('id')->on('users'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('countries'); }
Имя внешнего ключа должно быть - название владеющей модели (родительской таблицы в единственном числе) плюс _id (тут 'user_id') или указывать явно в связывающем методе модели.
Миграция - создания дополнительного, связывающего поля в существующую таблицу с внешним ключом.
public function up() { Schema::table('posts', function (Blueprint $table) { $table->integer('user_id')->unsigned()->default(1); $table->foreign('user_id')->references('id')->on('users'); }); } public function down() { Schema::table('posts', function ($table) { $table->dropForeign('posts_user_id_foreign'); $table->dropColumn('user_id'); }); }
Миграция – создание связывающей таблицы (для связи многие ко многим).
Пример связывания таблиц roles и users.
Создаем таблицу role_user. Название ее не случайно - указываются две связываемые таблицы roles и users через нижнее подчеркивание в единичном числе. Или же произвольное название и тогда указывается явно в модели, в связывающем методе belongsToMany() в качестве второго аргумента.
public function up() { Schema::create('role_user', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users'); $table->integer('role_id')->unsigned(); $table->foreign('role_id')->references('id')->on('roles'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('role_user'); }
Выборка данных, создание связывающих методов моделей.
Связь "один к одному".
Например, в таблице countries создан внешний ключ для связи с таблицей users. В таком случае, таблица users является родительской и нужно создать метод hasOne() в классе ее модели User, а для модели Country создать метод belongsTo().Создание метода для модели User:
public function country() { return $this->hasOne('App\Country'); }
Eloquent (реализация шаблона ActiveRecord в Larave) считает, что внешний ключ отношения называется по имени модели. В данном случае предполагается, что это user_id. Если вы хотите перекрыть стандартное имя, передайте второй параметр методу hasOne():
return $this->hasOne('App\Country', 'foreign_key');
Получение связи (в таблице 'countries') для пользователя с id=1:
$user = User::find(1); $country = $user->country; $name = $country->name; //Ukraineвызываем не метод, а одноименное динамическое свойство.
Создание обратной связи (для таблицы с которой связывали).
Метод модели:
public function user() { return $this->belongsTo('App\User'); }
получение данных аналогичное:
$country = Country::find(1); $user = $country->user; $name = $user->name; //Vova
Связь "Один ко многим".
На примере связи «один автор – несколько постов». АВТОР (таблица users) – ПОСТЫ (таблица posts).
Для этого должен быть создан внешний ключ для таблицы постов. После этого в модели User можно использовать метод hasMany(), а в модели Post метод belongsTo().
Создание метода для модели User:
public function posts() { return $this->hasMany('App\Post'); }
Получение данных:
$user = User::find(1); $posts = $user->posts; foreach ($posts as $post) { // }вызываем не метод, а одноименное динамическое свойство.
Создание обратной связи (для таблицы с которой связывали).
Метод модели Post:
public function User() { return $this->belongsTo('App\User'); }
получение данных:
$post = Post::find(3); $user = $post->user; echo $user->name;
Связь "Многие ко многим".
Связываем две таблицы: roles и users создав таблицу с названием role_user. При использовании произвольного имени связывающей таблицы- название указывается явно вторым аргументом в методе belongsToMany():
return $this->belongsToMany('App\Role', 'user_roles');так же можно перекрыть имена ключей по умолчанию:
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
Создание метода.
Для модели User:
public function roles() { return $this->belongsToMany('App\Role'); }
Для модели Role:
public function users(){ return $this->belongsToMany('App\User'); }
Получение данных:
$user = User::find(1); $roles = $user->roles; foreach ($roles as $role) { // }как и для других связей, вызываем не метод, а одноименное динамическое свойство "roles".
Для обратной связи (другой модели), получение данных проводится аналогично.
Проверка связей при выборке.
Есть возможность отобрать данные из таблицы, только те, которые имеют связь с другой, указанной таблицей.Например нужно отобрать всех пользователей, которые написали какие-то посты (имеют связь с таблицей постов – в таблице posts поле user_id соответствует id пользователя):
$users = User::has('posts')->get(); foreach ($users as $user) { echo $user->name; }
можно указать кол-во связей, которое должно быть:
$users = User::has('posts', '>=', '2')->get();
можно добавить произвольное условие выборки:
$users = User::whereHas('posts', function($q){ $q->where('description','like','Описание-%'); })->get();
Если использовать не динамическое свойство а метод связи.
В таком случае получаем HasMany Object, для получения данных из которого нужно использовать конструктор запросов:$user = User::find(1); $postsHasMany = $user->post(); $posts = $postsHasMany->where('description','like','Описание-%')->get(); foreach ($posts as $post) { // }то есть можно создать дополнительные условия для выборки.
При использовании могут возникнуть конфликты если в условии используются поля с одинаковыми названиями для разных таблиц. В таком случае нужно уточнять к какой таблице относится поле. Например:
->where('roles.id','<','10')
Вставка данных.
Для того, чтобы автоматически заполнялись связывающие поля между таблицами, для вставки данных нужно использовать специальные методы (аналогичные для любых типов связей):
Один из 2-х способов:
$user = User::find(1); $user->posts()->create([ 'description' => 'Описание', 'text' => 'text', 'user_id' => $user->id ]);или
$user = User::find(1); $post = new Post([ 'description' => 'Описание', 'text' => 'text', ]); $user->posts()->save($post);
При использовании метода save() «массовой вставки» может появиться ошибка
MassAssignmentException in Model.php line 225
это значит, что нужно разрешить вставку указанных полей в модели – метод $fillable().
Сохранить несколько связанных моделей можно так:
$posts = [ new Post(['description' => 'Описание 1', 'text' => 'text']), new Post(['description' => 'Описание 2', 'text' => 'text']), new Post(['description' => 'Описание 3', 'text' => 'text']), ]; $user = User::find(1); $user->posts()->saveMany($posts);
Аналогично и вставка для связи многие ко многим (через связывающую таблицу).
Пример.
Нужно создать пользователя с указанными данными и присвоить ему статус админа (в таблице roles соответствует id=1):
$user = new User(['name' => 'Masha', 'email' => 'email']); $role = Role::find(1); $role->users()->save($user);или
$role = Role::find(1); $role->users()->create([ 'name' => 'Pasha', 'email' => 'email2' ]);в данном случае, создастся указанный пользователь в таблице users, а так же в связывающей таблице “role_user” создастся связывающая запись.
Обновление данных.
Например нужно для пользователя под id=1 (таблица users) обновить поле 'text' из связанной таблицы постов (posts)
$user = User::find(1); $user->posts()->where('id',2)->update(['text'=>'new text']);
- сначала получаем модель нужного пользователя;
- для данной модели вызываем связывающий метод;
- т.к. один пользователь может иметь несколько записей, оператором where уточняем какой именно пост (id=2) нужно изменить;
- в методе update() указываем поля и их новые значения.
Читайте так же про методы, позволяющие изменять значения связывающих полей/таблиц в моей заметке.
ответ на комментарий Владимир от 05.02.2018