📚

【Laravel 11】多対多のポリモーフィックでのattach, syncメソッドでイベントを発火させる

2024/05/24に公開

Laravelで多対多にすることってよくあると思います。
ドキュメントとほぼ同じですが以下のような構造。

今回はポリモーフィックでの多対多です。(普通の多対多でも手順はほぼ同じです)

posts
    id - integer
    name - string
 
videos
    id - integer
    name - string
 
tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string
    order - int    #creating時にこれを更新したい

この postsvideos が作成された時点で、 taggable#order を更新したいとします。
その際に検討するのがEventsです。

class Taggable extends Model
{
    protected static function booted(): void
    {
        static::creating(function (User $user) {
            // orderを更新する処理
        });
    }
}

しかし、残念ながらこれは発火しません。

$post = Post::factory()->create();
$video = Video::factory()->create();
$post->attach($video)  // Eventは発火しない

中間テーブルのEventを発火させるにはDefining Custom Intermediate Table Modelsを定義する必要があります。

Custom many-to-many pivot models should extend the Illuminate\Database\Eloquent\Relations\Pivot class while custom polymorphic many-to-many pivot models should extend the Illuminate\Database\Eloquent\Relations\MorphPivot class.

ドキュメントでは以下のように使い分けて中間テーブルのクラスに継承させろと書いてあります。

普通の多対多の時 Illuminate\Database\Eloquent\Relations\Pivot
ポリモーフィックでの多対多の時 Illuminate\Database\Eloquent\Relations\MorphPivot
use Illuminate\Database\Eloquent\Relations\MorphPivot;

// MorphPivotを継承させる
class Taggable extends MorphPivot
{
    // 「pivotは本来単数系で、ポリモーフィックは複数形」がLaravelのデフォルト定義なのだが
    // 競合してエラーになるのでテーブル名を明示している。
    protected $table = 'taggables';

    protected static function booted()
    {
        static::creating(function ($taggable) {
            // orderを更新する処理
        });
    }
}
class Tag extends Model
{
    public function videos():
    {
        // using(Taggable::class)->withPivot('order')を追加
        return $this->morphedByMany(Video::class, 'taggable')->using(Taggable::class)->withPivot('order');
    }
}

以上です。

Discussion