#09 Laravel基礎
多対多リレーションとポリモーフィック
多対多リレーションとは
「投稿はタグを複数持てる」「タグは複数の投稿に付けられる」という関係が多対多です。これには中間テーブル(ピボットテーブル)が必要です。
中間テーブルのマイグレーション
<?php
// 📁 database/migrations/xxxx_create_post_tag_table.php
// ← テーブル名はアルファベット順で単数形を_で結ぶ慣習
Schema::create('post_tag', function (Blueprint $table) {
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['post_id', 'tag_id']); // 複合主キー
});
belongsToMany の定義
<?php
// 📁 app/Models/Post.php
class Post extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class);
// デフォルトで post_tag テーブルを使用する
}
}
<?php
// 📁 app/Models/Tag.php
class Tag extends Model
{
public function posts()
{
return $this->belongsToMany(Post::class);
}
}
データの操作
関連づける(attach)
$post = Post::find(1);
// タグID 1, 2, 3 を関連づける
$post->tags()->attach([1, 2, 3]);
関連を削除する(detach)
// タグID 2 の関連を削除
$post->tags()->detach(2);
// すべての関連を削除
$post->tags()->detach();
差分を同期する(sync)
フォームのチェックボックスなど「送られてきたIDの集合が最終状態」の場合に使います。
// 送られてきたタグIDに同期(不要なものを消し、新しいものを追加)
$post->tags()->sync($request->input('tag_ids', []));
中間テーブルに追加カラムを持つ場合
「いつ関連づけたか」などを中間テーブルに持たせる場合は withPivot() を使います。
// 📁 app/Models/Post.php
public function tags()
{
return $this->belongsToMany(Tag::class)
->withPivot('note') // 中間テーブルの追加カラム
->withTimestamps(); // created_at, updated_at を自動管理
}
アクセス方法:
foreach ($post->tags as $tag) {
echo $tag->pivot->note; // 中間テーブルの値
}
ポリモーフィックリレーション
「コメントを投稿にもビデオにも付けたい」という場合にポリモーフィックリレーションが使えます。同じ comments テーブルを複数のモデルで共有します。
マイグレーション
<?php
// 📁 database/migrations/xxxx_create_comments_table.php
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->morphs('commentable');
// → commentable_id(INT)と commentable_type(VARCHAR)が作られる
$table->foreignId('user_id')->constrained();
$table->timestamps();
});
モデルの定義
<?php
// 📁 app/Models/Comment.php
class Comment extends Model
{
public function commentable()
{
return $this->morphTo(); // 動的に親モデルを返す
}
}
<?php
// 📁 app/Models/Post.php
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
<?php
// 📁 app/Models/Video.php
class Video extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
使い方
// 投稿にコメントを追加
$post->comments()->create(['body' => 'よい記事です', 'user_id' => 1]);
// ビデオにコメントを追加
$video->comments()->create(['body' => '参考になりました', 'user_id' => 2]);
// コメントから親を取得
$comment = Comment::find(1);
$parent = $comment->commentable; // Post または Video が返る
まとめ
belongsToMany()で多対多リレーションを定義する- 中間テーブルのマイグレーションはアルファベット順で
post_tagのように命名する attach(),detach(),sync()で中間テーブルのデータを操作する- ポリモーフィックは
morphs('commentable')+morphTo()/morphMany()の組み合わせ
次回はソフトデリートとオブザーバーを学びます。