はじめに
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対多同士に分ける整理しやすいかもですね。
コメント