端くれプログラマの備忘録 Laravel [Laravel] Many to Many Polymorphic Relations の使いどころ

[Laravel] Many to Many Polymorphic Relations の使いどころ

Laravel Eloquentにおける Many to Many Polymorphic Relations(多対多のポリモーフィックリレーション)は、複数のモデル間で多対多のリレーションを柔軟に表現できる仕組みです。このリレーションを使うと、1つの中間テーブルを使用して複数の異なるモデルと関連付けを管理できます。

基本概念

  • 多対多リレーション:
    • 中間テーブルを介して、2つのモデルが多対多で関連付けられる関係。
  • ポリモーフィックリレーション:
    • 1つのモデルが複数の異なるモデルと動的に関連付けられる仕組み。
  • 多対多ポリモーフィックリレーション:
    • 中間テーブルを共有しつつ、複数の異なるモデルと多対多リレーションを表現できる関係。

例: タグシステム

複数の異なるモデル(例: PostVideo)に共通のタグを付けるシナリオを考えます。

  • モデル:
    • Post(ブログ記事)
    • Video(動画)
    • Tag(タグ)
  • 中間テーブル:
    • taggables(どのタグがどのモデルに関連付けられているかを管理)

データベース構造

1. tags テーブル:

    • id: タグの一意識別子
    • name: タグ名

    2. taggables 中間テーブル:

      • tag_id: タグのID
      • taggable_id: リレーション先モデルのID
      • taggable_type: リレーション先モデルのクラス名(例: App\Models\PostApp\Models\Video

      Eloquentモデルの設定

      1. Tag モデル

      <?php
      
      namespace App\Models;
      
      use Illuminate\Database\Eloquent\Model;
      
      class Tag extends Model
      {
          public function posts()
          {
              return $this->morphedByMany(Post::class, 'taggable');
          }
      
          public function videos()
          {
              return $this->morphedByMany(Video::class, 'taggable');
          }
      }

      2. Post モデル

      <?php
      
      namespace App\Models;
      
      use Illuminate\Database\Eloquent\Model;
      
      class Post extends Model
      {
          public function tags()
          {
              return $this->morphToMany(Tag::class, 'taggable');
          }
      }

      3. Video モデル

      <?php
      
      namespace App\Models;
      
      use Illuminate\Database\Eloquent\Model;
      
      class Video extends Model
      {
          public function tags()
          {
              return $this->morphToMany(Tag::class, 'taggable');
          }
      }

      使用例

      1. タグの関連付け

      $post = Post::find(1);
      $tag = Tag::find(1);
      
      // 投稿にタグを関連付け
      $post->tags()->attach($tag->id);
      
      // 動画にも同じタグを関連付け
      $video = Video::find(1);
      $video->tags()->attach($tag->id);

      2. タグから関連アイテムを取得

      $tag = Tag::find(1);
      
      // このタグが関連付けられたすべての投稿
      $posts = $tag->posts;
      
      // このタグが関連付けられたすべての動画
      $videos = $tag->videos;

      3. 既存のリレーションの解除

      $post->tags()->detach($tag->id); // 投稿から特定のタグを解除

      4. タグの同期(新しいタグと置き換え)

      $post->tags()->sync([2, 3, 4]); // タグID 2, 3, 4に置き換え

      メリット

      1. 柔軟性: 中間テーブルを共有することで、コードの再利用性が高まります。
      2. スケーラビリティ: 新しいモデルを追加しても、既存のリレーションを変更せずに管理可能です。
      3. 一貫性: LaravelのEloquentメソッド(attach, detach, sync など)が使えるため、操作が簡単。

      注意点

      • 中間テーブルの taggable_type カラムには、モデルの完全修飾クラス名(例: App\Models\Post)が保存されるため、リファクタリングで名前空間やクラス名を変更する際には注意が必要です。
      • 適切なインデックス(taggable_idtaggable_type)を設定するとパフォーマンスが向上します。

      この仕組みを利用すると、タグ付けシステムやコメントシステムなど、モデル間で共有可能なリレーションを効率的に実装できます。