Eloquent のリレーション(多 対 多)

2019/01/08

前回、Eloquent のリレーションで「1対1」と「1対多」についてやりました。

今回は「多 対 多」のリレーションを Eloquent でやります。

 

演習

例えば、講習テーブルと学生テーブルの関係を DB で構築するとします。

講習テーブルにある各講習には複数の学生が受講します。

逆に学生テーブルにある各学生も複数の科目を受講します。

よって、講習テーブルと学生テーブルの関係は「多 対 多」の関係にあります。

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

 

中間テーブルとは

例)ブログの記事(postsテーブル)とタグ(tagsテーブル)
これらの関係性をテーブルで表そうとすると、post_id、tag_idのカラム内に複数の id を書かなくてはいけません。しかし、カラムにそうのような書き方はできません。そこで、中間テーブル(posts_tagsテーブル)を設けて、どの記事がどのタグと結びついているかの関係性を示していきます。

 

手順

 

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

 
講習(courses)テーブル

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


$ php artisan make:migration create_courses_table --create=courses

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


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

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


$ php artisan migrate

 
学生(students)テーブルを作成

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


$ php artisan make:migration create_students_table --create=students

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


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

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


$ php artisan migrate

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

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


$ php artisan make:migration create_course_student_table --create=course_student

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


:
public function up()
{
Schema::create('course_student', function (Blueprint $table) {
$table->unsignedInteger('course_id');
$table->unsignedInteger('student_id');
$table->primary(['course_id','student_id']);

// 外部キー制約
$table->foreign('course_id')->references('id')->on('courses')->onDelete('cascade');
$table->foreign('student_id')->references('id')->on('students')->onDelete('cascade');
});
}
:

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


$ php artisan migrate

 

2)モデルを使ってリレーションを定義

 
Courseモデル

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


$ php artisan make:model Course

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


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

ポイント

両モデルには「belongsToMany」で定義します。また、メソッド名は複数形です。

 
Studentモデル

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


$ php artisan make:model Student

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


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

 

3)シーダー生成&実行

 
coursesテーブル

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


$ php artisan make:seed CourseTableSeeder

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


# CourseTableSeeder.php
:
public function run()
{
  // 1レコード
  $course = new \App\Course([
  'name' => 'PHP'
  ]);
  $course->save();

  // 2レコード
  $course = new \App\Course([
  'name' => 'JavaScript'
  ]);
  $course->save();
:

 
3.DatabaseSeeder.php につなげる


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

 
studentsテーブル

coursesテーブルと同様の手順で作成します。

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

coursesテーブルと同様の手順で作成します。

 
最後に artisan コマンドでシーダーを実行して3つのテーブルに各サンプルデータをテーブルにインサートします。


php artisan db:seed

 

4)Tinkerを使って動作確認

最後に tinker を起動して Eloquent で双方向からデータを取得するテストを行ってみます。

tinker についてわからない方は以下の記事をご参考ください。

 
学生モデルから講習モデルのデータを引っ張る


>>> App\Student::find(1)->courses;

 
講習モデルから学生モデルのデータを引っ張る


>>> App\Course::find(1)->students;

 
上記のように Laravel で 「多 対 多」のリレーションができたことが確認できました。

以上です。

本庄マサノリ

仕事でLaravelを使っています。気づいたことや新しい発見など情報を発信していきます。メールはこちら

 

-実践知識