この記事では、LaravelのモデルのManyToManyのリレーションについて紹介します。
ManyToManyリレーションとは
MayToManyリレーションとは、2つのモデル間で多対多の関連性を表現するために使用されます。
例えば、articlesとtagsというモデルがある場合、articlesはそれぞれは複数のtagsに紐づいていて、tagsのそれぞれも複数のarticlestに紐づいている時にarticlesとtagsはManyToManyリレーションになります。
中間テーブル
ManyToManyリレーションでは中間テーブルが存在します。このテーブルは2つのモデル間のリレーションシップを記録するために使用されます。
例えば、aritclesとtagsのテーブルの中間テーブルarticle_tagは次のように表現されます。
article_id | int |
tag_id | int |
aricle_idとtag_idはそれぞれarticlesとtagsの主キーの外部キーです
このように中間テーブルでは関連する2つのテーブルの主キーの組み合わせを保存することでリレーションを管理します。
LaravelでのManyToManyリレーションの実装
実際にLaravelでManyToManyリレーションのモデルの実装例を紹介します。
マイグレーションを作成
今回はarticlesとtasと中間テーブルとしてarticle_tagを作成します。それぞれのマイグレーションファイルは以下になります。
articlesテーブル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('articles', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('articles'); } }; |
tagsテーブル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('tags', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('tags'); } }; |
article_tagテーブル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('article_tag', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('article_id'); $table->unsignedBigInteger('tag_id'); $table->timestamps(); $table->foreign('article_id') ->references('id') ->on('articles') ->onDelete('cascade'); $table->foreign('tag_id') ->references('id') ->on('tags') ->onDelete('cascade'); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('article_tag'); } }; |
article_tagテーブルはaritle_id、tag_idのカラムを持ちます。それぞれarticlesのid(主キー)、tagsのid(主キー)への外部キーです。
マイグレーションファイルを作成したら、php artisan migrate
でマイグレーションを実行します。
モデルを作成
次にモデルを作成します。
app/Models/Article.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Article extends Model { use HasFactory; public function tags() { return $this->belongsToMany(Tag::class); } } |
app/Models/Tag.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Tag extends Model { use HasFactory; public function articles() { return $this->belongsToMany(Article::class); } } |
モデルのデータを作成
次にArticle、Tagモデルのデータをphp artisan tinker
で以下のように作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
> $article = new Article(); [!] Aliasing 'Article' to 'App\Models\Article' for this Tinker session. = App\Models\Article {#3759} > $article->title = 'タイトル'; = "タイトル" > $article->body = '内容'; = "内容" > $article->save(); = true > $tag1 = new Tag(); [!] Aliasing 'Tag' to 'App\Models\Tag' for this Tinker session. = App\Models\Tag {#4496} > $tag1->name = 'タグ1'; = "タグ1" > $tag1->save(); = true > $tag2 = new Tag(); = App\Models\Tag {#4093} > $tag2->name = 'タグ2'; = "タグ2" > $tag2->save(); = true |
Articleのデータを1つ、Tagのデータを2つ作成しました。
ManyToManyリレーションを構築する
最後にManyToManyリレーションを構築します。
articleにtag1とtag2を紐付けたい場合は次のようにattach
メソッドを使用します。
1 |
$article->tags()->attach([$tag1->id, $tag2->id]); |
その後articleに紐づいているTagモデルのデータを取り出したい時には$article->tags
とすれば取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$article->tags; = Illuminate\Database\Eloquent\Collection {#3768 all: [ App\Models\Tag {#4724 id: 3, name: "タグ1", created_at: "2023-04-07 13:20:07", updated_at: "2023-04-07 13:20:07", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4722 article_id: 2, tag_id: 3, }, }, App\Models\Tag {#4720 id: 4, name: "タグ2", created_at: "2023-04-07 13:20:38", updated_at: "2023-04-07 13:20:38", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4719 article_id: 2, tag_id: 4, }, }, ], } |
紐付けを解除したい場合はdetach
メソッドを使用します。
1 |
$article->tags()->detach([$tag1->id]); |
最後にManyToManyのリレーションを特定のリストに更新したい場合のケースとしてsync
メソッドを紹介します。
例えば、新たにtag3を作成して、articleに紐づくtagはtag1とtag3にしたい場合は次のように表現します。
1 |
$article->tags()->sync([$tag1->id, $tag3->id]) |
この場合、tag2の連結は解除されて、新たにtag1とtag3の関連付けがされます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
> $article->tags; = Illuminate\Database\Eloquent\Collection {#4713 all: [ App\Models\Tag {#4708 id: 5, name: "タグ3", created_at: "2023-04-07 13:44:29", updated_at: "2023-04-07 13:44:29", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4711 article_id: 2, tag_id: 5, }, }, App\Models\Tag {#4710 id: 3, name: "タグ1", created_at: "2023-04-07 13:20:07", updated_at: "2023-04-07 13:20:07", pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4712 article_id: 2, tag_id: 3, }, }, ], } |
まとめ
この記事ではManyToManyリレーションの説明とLaravelでの実装の仕方についてまとめました。
Laravelではattach
,detach
,sync
メソッドでManyToManyのリレーションを簡単に関連づけることができます。
本記事が参考になれば幸いです。