Back to Course |
Laravel 11 Eloquent: Expert Level

Advanced Polymorphic: Many-to-Many

Now, let's look at the polymorphic relation, which is a bit more complex: a many-to-many polymorphic relation.

Suppose we look at the photos table from a previous lesson example. What if you want the same photo to belong to both a task and a user, or maybe to more models like the posts? This sounds like a many-to-many relationship, doesn't it?


Structure

So, what do we do for the polymorphic photos table? We remove the morph fields and create a separate pivot table called photoables.

Schema::create('photos', function (Blueprint $table) {
$table->id();
$table->string('filename');
$table->morphs('photoable');
$table->timestamps();
});

The photoables pivot table will have a foreign key column to the photos table and the same two morph columns ID and type.

Schema::create('photoables', function (Blueprint $table) {
$table->foreignId('photo_id')->constrained();
$table->morphs('photoable');
});

Then, in the Photo Model, we don't need the photoable morph relation. Instead, we must define each Model's morphedByMany relation.

app/Models/Photo.php:

use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Photo extends Model
{
public function photoable(): MorphTo
{
return $this->morphTo();
}
 
public function users(): MorphToMany
{
return $this->morphedByMany(User::class, 'photoable');
}
 
public function tasks(): MorphToMany
{
return $this->morphedByMany(Task::class, 'photoable');
}
}

And, instead of the morphMany relation, we must use morphToMany in other Models.

app/Models/Task.php:

use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class Task extends Model
{
public function photos(): MorphMany
{
return $this->morphMany(Photo::class, 'photoable');
}
 
public function photos(): MorphToMany
{
return $this->morphToMany(Photo::class, 'photoable');
}
}

app/Models/User.php:

use Illuminate\Database\Eloquent\Relations\MorphToMany;
 
class User extends Authenticatable
{
// ...
 
public function photos(): MorphMany
{
return $this->morphMany(Photo::class, 'photoable');
}
 
public function photos(): MorphToMany
{
return $this->morphToMany(Photo::class, 'photoable');
}
}

In a typical many-to-many relationship, it doesn't matter who the parent or the child is. They both belong to many. In this case, the relationship matters.

I kept everything the same when showing the result, only re-seeding the database.

This is a more complex polymorphic relation, but still with the same idea that you have two morph columns that define the polymorphism.