Eloquentのリレーション(多対多)について調べてみた

Laravel/PHP
スポンサーリンク
  1. はじめに
  2. 開発状況(Version)
  3. 多対多の関係
    1. 例えば
  4. マイグレーションファイルを生成&実行
    1. 動画(movies)テーブル
      1. 1,artisanコマンドでスケルトンを生成
      2. 2,マイグレーションファイルを編集
      3. 3,マイグレーション実行
    2. ユーザー(users)テーブルを作成
      1. 1,artisanコマンドでスケルトンを生成
      2. 2,マイグレーションファイルを編集
      3. 3,マイグレーション実行
    3. 中間(movie_user)テーブルを作成
      1. 1,artisanコマンドでスケルトンを生成
      2. 2,マイグレーションファイルを編集
      3. 3,マイグレーション実行
  5. モデルを使用してリレーションを定義
    1. Movieモデル
      1. 1,artisanコマンドでモデルのスケルトンを作成
      2. 2,モデルにリレーションを定義
    2. Userモデル
      1. 1,artisanコマンドでモデルのスケルトンを作成
      2. 2,モデルにリレーションを定義
  6. シーダー生成&実行
    1. moviesテーブル
      1. 1,artisanコマンドでモデルのスケルトンを作成
      2. 2,シーダーファイルを作成
      3. 3,DatabaseSeeder.phpに繋げる
    2. Usersテーブル
      1. 1,artisanコマンドでモデルのスケルトンを作成
      2. 2,シーダーファイルを作成
      3. 3,DatabaseSeeder.phpに繋げる
    3. movie_userテーブル(中間テーブル)
    4. テーブルに各サンプルデータをテーブルにインサート
  7. Tinkerを使用して動作確認
    1. ユーザーモデルから動画モデルのデータを引っ張る
    2. 動画モデルからユーザーモデルのデータを引っ張る
  8. 最後
  9. 参考記事

はじめに

Laravelで開発中に、リレーションについて、曖昧な箇所や、なんとなく実装していた箇所ががあったため、調べ直してみました。多対多編!

開発状況(Version)

macOS Big Sur
Laravel 6系
PHP 7系

多対多の関係

中間テーブルを設けて、「どのテーブル」と「どのテーブル」が結びついているかの関係性を示します。

例えば

動画テーブルと、ユーザーテーブルの関係をDBで構築するとします。

動画テーブルにある、各動画には、複数のユーザーが登録しています。

逆に、ユーザーテーブルにある、各ユーザーも複数の動画を登録します。

ということは、動画テーブルとユーザーテーブルは、「多対多」の関係にあります。

※多対多リレーションでは、双方に外部キーを設け、中間テーブルを利用します。

※中間テーブルの命名は、アルファベット順で単数形に直し、テーブル名を並べるというルールがあります。

マイグレーションファイルを生成&実行

動画(movies)テーブル

1,artisanコマンドでスケルトンを生成

$ php artisan make:migration create_movies_table --create=movies

2,マイグレーションファイルを編集

ーーーーーー抜粋ーーーーーー

public function up()
{
Schema::create('movies', function (Blueprint $table) {
  $table->increments('id');
  $table->string('name');
  $table->timestamps();
});
}

ーーーーーー抜粋ーーーーーー

3,マイグレーション実行

$ php artisan migrate

ユーザー(users)テーブルを作成

1,artisanコマンドでスケルトンを生成

$ php artisan make:migration create_users_table --create=users

2,マイグレーションファイルを編集

ーーーーーー抜粋ーーーーーー

public function up()
{
Schema::create('users', function (Blueprint $table) {
  $table->increments('id');
  $table->string('name');
  $table->timestamps();
});
}

ーーーーーー抜粋ーーーーーー

3,マイグレーション実行

$ php artisan migrate

中間(movie_user)テーブルを作成

1,artisanコマンドでスケルトンを生成

$ php artisan make:migration create_movie_user_table --create=movie_user

2,マイグレーションファイルを編集

ーーーーーー抜粋ーーーーーー

public function up()
{
Schema::create('movie_user', function (Blueprint $table) {
$table->unsignedInteger('movie_id');
$table->unsignedInteger('user_id');
$table->primary(['movie_id','user_id']);

// 外部キー制約
$table->foreign('movie_id')->references('id')->on('movies')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}

ーーーーーー抜粋ーーーーーー

3,マイグレーション実行

$ php artisan migrate

モデルを使用してリレーションを定義

Movieモデル

1,artisanコマンドでモデルのスケルトンを作成

$ php artisan make:model Movie

2,モデルにリレーションを定義

ーーーーーー抜粋ーーーーーー

class Movie extends Model
{
  public function users()
  {
    return $this->belongsToMany('App\Student');
  }
}

ーーーーーー抜粋ーーーーーー

※両モデルは「belongToMany」で定義します。メソッド名は複数形です。

Userモデル

1,artisanコマンドでモデルのスケルトンを作成

$ php artisan make:model User

2,モデルにリレーションを定義

ーーーーーー抜粋ーーーーーー

class User extends Model
{
  public function movies()
  {
    return $this->belongsToMany('App\Student');
  }
}

ーーーーーー抜粋ーーーーーー

※両モデルは「belongToMany」で定義します。メソッド名は複数形です。

シーダー生成&実行

moviesテーブル

1,artisanコマンドでモデルのスケルトンを作成

$ php artisan make:seed MovieTableSeeder

2,シーダーファイルを作成

//MovieTableSeeder.php

ーーーーーー抜粋ーーーーーー

public function run()
{
  // 1レコード
  $movie = new \App\movie([
  'name' => 'Aチャンネル'
  ]);
  $movie->save();

   // 2レコード
  $movie = new \App\movie([
  'name' => 'Bチャンネル'
  ]);
  $movie->save();

 // 3レコード
  $movie = new \App\movie([
  'name' => 'Cチャンネル'
  ]);
  $movie->save();

 // 4レコード
  $movie = new \App\movie([
  'name' => 'Dチャンネル'
  ]);
  $movie->save();

 // 5レコード
  $movie = new \App\movie([
  'name' => 'Eチャンネル'
  ]);
  $movie->save();

 // 6レコード
  $movie = new \App\movie([
  'name' => 'Fチャンネル'
  ]);
  $movie->save();

ーーーーーー抜粋ーーーーーー

3,DatabaseSeeder.phpに繋げる

ーーーーーー抜粋ーーーーーー

public function run()
{
$this->call(MovieTableSeeder::class);
}

ーーーーーー抜粋ーーーーーー

Usersテーブル

1,artisanコマンドでモデルのスケルトンを作成

$ php artisan make:seed UserTableSeeder

2,シーダーファイルを作成

//UserTableSeeder.php

ーーーーーー抜粋ーーーーーー

public function run()
{
  // 1レコード
  $user = new \App\user([
  'name' => '有川'
  ]);
  $user->save();

   // 2レコード
 $user = new \App\user([
  'name' => '松本'
  ]);
  $user->save();

 // 3レコード
  $user = new \App\user([
  'name' => '川下'
  ]);
  $user->save();

 // 4レコード
  $user = new \App\user([
  'name' => '北川'
  ]);
  $user->save();

 // 5レコード
 $user = new \App\user([
  'name' => '衛藤'
  ]);
  $user->save();

 // 6レコード
  $user = new \App\user([
  'name' => '河上'
  ]);
  $user->save();

ーーーーーー抜粋ーーーーーー

3,DatabaseSeeder.phpに繋げる

ーーーーーー抜粋ーーーーーー

public function run()
{
$this->call(UserTableSeeder::class);
}

ーーーーーー抜粋ーーーーーー

movie_userテーブル(中間テーブル)

movieテーブルや、userテーブルと同様に作成します。

テーブルに各サンプルデータをテーブルにインサート

$ php artisan db:seed

Tinkerを使用して動作確認

Tinkerを起動して、Eloquentで双方向からデータを取得するテストを行い確認します。

ユーザーモデルから動画モデルのデータを引っ張る

$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.2.34 — cli) by Justin Hileman
>>> App\User::find(1)->movies;
=> Illuminate\Database\Eloquent\Collection {#3548
    all: [
     App\Movie {#3544
      id: 1,
      name: "Aチャンネル",
      created_at: "2021-12-22 10:16:42",
      updated_at: "2021-12-22 10:16:42",
      pivot: Illuminat\Database\Eloquent\Relations\Pivot {3549
       user_id: 1,
       movie_id: 1,
     },
    },
   App\Movie {#3545
      id: 2,
      name: "Bチャンネル",
      created_at: "2021-12-22 10:16:42",
      updated_at: "2021-12-22 10:16:42",
      pivot: Illuminat\Database\Eloquent\Relations\Pivot {3542
       user_id: 1,
       movie_id: 2,
     },
    },

動画モデルからユーザーモデルのデータを引っ張る

$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.2.34 — cli) by Justin Hileman
>>> App\Movie::find(1)->users;
=> Illuminate\Database\Eloquent\Collection {#3538
    all: [
     App\Movie {#3534
      id: 1,
      name: "有川",
      created_at: "2021-12-22 10:16:42",
      updated_at: "2021-12-22 10:16:42",
      pivot: Illuminat\Database\Eloquent\Relations\Pivot {3539
       movie_id: 1,
       user_id: 1,
     },
    },
   App\Movie {#3535
      id: 2,
      name: "松本",
      created_at: "2021-12-22 10:16:42",
      updated_at: "2021-12-22 10:16:42",
      pivot: Illuminat\Database\Eloquent\Relations\Pivot {3532
       movie_id: 1,
       user_id: 2,
     },
    },

最後

多対多を纏めてみましたが、纏めながらも混乱する箇所があり、良い機会になりました。
多対多を、中間テーブルをはさみ、1対多同士に分ける整理しやすいかもですね。

参考記事

コメント