Laravelで多対多のリレーション
Laravelで多対多のリレーションがややこしかったので、ここにまとめて起きます。
今回は学生テーブルと科目テーブルで多対多のリレーションをしていきます。
外部キーを使う場合と使わない場合の2種類でやっていきます。
php artisan make:model Student --migration php artisan make:model Subject --migration php artisan make:migration create_student_subject_table php artisan make:seed StudentsTableSeeder php artisan make:seed SubjectsTableSeeder php artisan make:seed StudentSubjectTableSeeder
まず、マイグレーションファイルを作ります。
これでModelも同時に作られます。
次にマイグレーションファイルを編集していきます。
<?php //create_students_table public function up() { Schema::create('students', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }
<?php //create_subjects_table public function up() { Schema::create('subjects', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }
<?php //create_student_subject_table public function up() { Schema::create('student_subject', function (Blueprint $table) { $table->unsignedInteger('student_id'); $table->unsignedInteger('subject_id'); $table->primary(['student_id','subject_id']); $table->foreign('student_id')->references('id')->on('students')->onDelete('cascade'); $table->foreign('subject_id')->references('id')->on('subjects')->onDelete('cascade'); }); }
php artisan migrate
マイグレーション実行を実行してテーブルを作ります。
中間テーブルの命名規則は以下の通りです
・アルファベット順
・スネークケースでテーブル名の 単数形 を繋げる
次にシーダーで値を入れていきます。
<?php //php artisan make:seed StudentsTableSeeder public function run() { public function run() { DB::table('students')->insert([ 'name'=>'田中', 'created_at' => now(), 'updated_at' => now() ]); DB::table('students')->insert([ 'name'=>'鈴木', 'created_at' => now(), 'updated_at' => now() ]); DB::table('students')->insert([ 'name'=>'佐藤', 'created_at' => now(), 'updated_at' => now() ]); DB::table('students')->insert([ 'name'=>'高橋', 'created_at' => now(), 'updated_at' => now() ]); DB::table('students')->insert([ 'name'=>'渡辺', 'created_at' => now(), 'updated_at' => now() ]); } }
<?php //php artisan make:seed SubjectsTableSeeder public function run() { DB::table('subjects')->insert([ 'name'=>'国語', 'created_at' => now(), 'updated_at' => now() ]); DB::table('subjects')->insert([ 'name'=>'数学', 'created_at' => now(), 'updated_at' => now() ]); DB::table('subjects')->insert([ 'name'=>'英語', 'created_at' => now(), 'updated_at' => now() ]); DB::table('subjects')->insert([ 'name'=>'社会', 'created_at' => now(), 'updated_at' => now() ]); DB::table('subjects')->insert([ 'name'=>'音楽', 'created_at' => now(), 'updated_at' => now() ]);
<?php //php artisan make:seed StudentSubjectTableSeeder public function run() { DB::table('student_subject')->insert([ 'student_id'=>'1', 'subject_id' =>'1' ]); DB::table('student_subject')->insert([ 'student_id'=>'2', 'subject_id' =>'2' ]); DB::table('student_subject')->insert([ 'student_id'=>'3', 'subject_id' =>'3' ]); DB::table('student_subject')->insert([ 'student_id'=>'4', 'subject_id' =>'4' ]); DB::table('student_subject')->insert([ 'student_id'=>'5', 'subject_id' =>'5' ]); DB::table('student_subject')->insert([ 'student_id'=>'1', 'subject_id' =>'2' ]); DB::table('student_subject')->insert([ 'student_id'=>'1', 'subject_id' =>'3' ]);
これで以下の画像のようなDBが作成できたと思います。
これからは外部キーを使う場合と使わない場合で書き方が少し異なってきます。
外部キーを使う場合
モデルを編集します
<?php <?php //App\Student.php namespace App; use Illuminate\Database\Eloquent\Model; class Student extends Model { public function subjects(){ return $this->belongsToMany('App\Subject'); //return $this->belongsToMany('App\Subject','student_subject','student_id','subject_id'); 同じ意味 } }
対多リレーションはbelongsToManyメソッド呼び出しを記述することで定義します。
引数には相手のModel名をいれます。
これでtinkerで確認すると
>>> App\Student::find(1)->subjects; => Illuminate\Database\Eloquent\Collection {#3036 all: [ App\Subject {#3037 id: "1", name: "国語", created_at: "2020-05-05 18:58:58", updated_at: "2020-05-05 18:58:58", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3040 student_id: "1", subject_id: "1", }, }, App\Subject {#3038 id: "2", name: "数学", created_at: "2020-05-05 18:58:59", updated_at: "2020-05-05 18:58:59", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3034 student_id: "1", subject_id: "2", }, }, App\Subject {#3041 id: "3", name: "英語", updated_at: "2020-05-05 18:58:59", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3019 student_id: "1", subject_id: "3", }, }, ], }
ID1の学生が受講している科目のデータを取得できました。
今度は逆のパターンをやっていきます。
<?php <?php //App\Subject namespace App; use Illuminate\Database\Eloquent\Model; class Subject extends Model { public function students(){ return $this->belongsToMany('App\Students'); //return $this->belongsToMany('App\Subject','student_subject','subject_id','student_id'); 同じ意味 } }
これをtinkerで確認します。
>>> App\Subject::find(1)->students; => Illuminate\Database\Eloquent\Collection {#3036 id: "1", name: "田中", created_at: "2020-05-05 18:58:58", updated_at: "2020-05-05 18:58:58", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3034 subject_id: "1", student_id: "1", }, }, ], }
ID1の科目を受講している学生のデータを取得できました。
ちなみに中間テーブルに値を追加、削除する場合はattachとdetachを使います。
attachが追加
detachが削除。
<?php //App\Student.php namespace App; use Illuminate\Database\Eloquent\Model; class Student extends Model { public function subjects(){ return $this->belongsToMany('App\Subject'); } public function joinsubject(Int $student_id){ return $this->subjects()->attach($student_id); } public function removwsubject(Int $student_id){ return $this->subjects()->detach($student_id); } }
tinkerで実行してみます
App\Student::find(1)->joinsubject(4);
DBを確認してみると追加されてるとおもいます。
つぎに削除してみます。
App\Student::find(1)->removwsubject(4);
これでDBから消えました。
外部キーをを使わない場合
<?php //create_student_subject_table public function up() { Schema::create('student_subject', function (Blueprint $table) { $table->unsignedInteger('student_id'); $table->unsignedInteger('subject_id'); $table->primary(['student_id','subject_id']); // $table->foreign('student_id')->references('id')->on('students')->onDelete('cascade'); // $table->foreign('subject_id')->references('id')->on('subjects')->onDelete('cascade'); }); }
マイグレーションファイルの外部キー制約をコメントアウトします。
次にModelを変えていきます
<?php //App\Student.php namespace App; use Illuminate\Database\Eloquent\Model; class Student extends Model { public function subjects(){ //return $this->belongsToMany('App\Subject'); return $this->belongsToMany('App\Subject','student_subject','student_id','subject_id'); } public function joinsubject(Int $student_id){ return $this->subjects()->attach($student_id); } public function removwsubject(Int $student_id){ return $this->subjects()->detach($student_id); } }
先程の場合と違う方を使います。
第1引数には相手のModel名。
第2引数には中間テーブル。
第3引数には自モデルの主キーと関連づけるカラム名。
第3引数には相手ののモデルの主キーと関連づけるカラム名。
これらを明示的にしていするだけで外部キーを使わなくても大丈夫です。