Создание и использование связей между таблицами в Laravel 5.*

Пример создания файла миграции в консоли

Создать пустой файл миграции с названием CreateRoleTables:

# php artisan make:migration CreateRoleTables

Создать файл миграции с названием CreateRoleTables и определить в нем создание таблицы roles:

# 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']);
  1. сначала получаем модель нужного пользователя;
  2. для данной модели вызываем связывающий метод;
  3. т.к. один пользователь может иметь несколько записей, оператором where уточняем какой именно пост (id=2) нужно изменить;
  4. в методе update() указываем поля и их новые значения.

Если не указать условие (where), то изменены будут все строки связанные с данным пользователем.

Оригинал: https://klisl.com/laravel_relationships.html