初心者のプログラミング日記

プログラミング初心者の日記

プログラミングに関することを書いていきます。

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が作成できたと思います。

f:id:nasubiFX:20200506145305p:plain
学生テーブル
f:id:nasubiFX:20200506145325p:plain
科目テーブル
f:id:nasubiFX:20200506145405p:plain
中間テーブル

これからは外部キーを使う場合と使わない場合で書き方が少し異なってきます。

外部キーを使う場合

モデルを編集します

<?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引数には相手ののモデルの主キーと関連づけるカラム名

これらを明示的にしていするだけで外部キーを使わなくても大丈夫です。

参考記事
https://www.yuulinux.tokyo/15552/#Tinker