Polymorphic Many To Many

What is Polymorphic Many To Many?

A Polymorphic Many To Many relationship allows multiple models to share a common relationship with another model using a single pivot table.

👉 In this example:

  • A Playlist can contain many Songs.
  • A Playlist can also contain many Videos.
  • A Playlist can also contain many Podcasts.
  • But instead of creating song_playlist, video_playlist, podcast_playlist, we use one single pivot table: playlistables.
Playlist ↔ Songs  
Playlist ↔ Videos  
Playlist ↔ Podcasts

Flow diagram


Migration:

Use this command to generate a migration for creating a tables:

php artisan make:migration create_polymorphic_many_to_many_tables


Migration File

public function up(): void
{
    // Playlists
    Schema::create('playlists', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });

    // Songs
    Schema::create('songs', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('artist')->nullable();
        $table->timestamps();
    });

    // Videos
    Schema::create('videos', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('channel')->nullable();
        $table->timestamps();
    });

    // Podcasts
    Schema::create('podcasts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('host')->nullable();
        $table->timestamps();
    });

    // Polymorphic pivot table
    Schema::create('playlistables', function (Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('playlist_id');
        $table->unsignedBigInteger('playlistable_id');
        $table->string('playlistable_type');
        $table->timestamps();

        $table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade');
    });
}

public function down(): void
{
    Schema::dropIfExists('playlistables');
    Schema::dropIfExists('podcasts');
    Schema::dropIfExists('videos');
    Schema::dropIfExists('songs');
    Schema::dropIfExists('playlists');
}


Run Migration

php artisan migrate

Tables

songs table
id title artist created_at updated_at
videos table
id title channel created_at updated_at
podcasts table
id title host created_at updated_at
playlists table
id name created_at updated_at
playlistables table
id playlist_id playlistable_id playlistable_type created_at updated_at

Model:

Generate a model with Artisan:

php artisan make:model Playlist
php artisan make:model Song
php artisan make:model Video
php artisan make:model Podcast

This generates 4 files Playlist.php , Song.php ,Video.php and Podcast.php


Song.php

namespace App\Models;

use App\Models\Playlist;
use Illuminate\Database\Eloquent\Model;

class Song extends Model
{
    protected $fillable = ['title', 'artist'];

    public function playlists()
    {
        return $this->morphToMany(Playlist::class, 'playlistable');
    }
}


Video.php

namespace App\Models;

use App\Models\Playlist;
use Illuminate\Database\Eloquent\Model;

class Video extends Model
{
    protected $fillable = ['title', 'channel'];

    public function playlists()
    {
        return $this->morphToMany(Playlist::class, 'playlistable');
    }
}


Podcast.php

namespace App\Models;

use App\Models\Playlist;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    protected $fillable = ['title', 'host'];

    public function playlists()
    {
        return $this->morphToMany(Playlist::class, 'playlistable');
    }
}


Playlist.php

namespace App\Models;

use App\Models\Song;
use App\Models\Video;
use App\Models\Podcast;
use Illuminate\Database\Eloquent\Model;

class Playlist extends Model
{
    protected $fillable = ['name'];

    public function songs()
    {
        return $this->morphedByMany(Song::class, 'playlistable');
    }

    public function videos()
    {
        return $this->morphedByMany(Video::class, 'playlistable');
    }

    public function podcasts()
    {
        return $this->morphedByMany(Podcast::class, 'playlistable');
    }
}

CRUD Examples

Create

use App\Models\Song;
use App\Models\Video;
use App\Models\Podcast;
use App\Models\Playlist;


// Create Playlist
$playlist = Playlist::create(['name' => 'My Favorites']);

// Create a Song and attach to Playlist
$song = Song::create(['title' => 'Shape of You', 'artist' => 'Ed Sheeran']);
$playlist->songs()->attach($song->id);

// Create a Video and attach to Playlist
$video = Video::create(['title' => 'Laravel Tutorial', 'channel' => 'Code Academy']);
$playlist->videos()->attach($video->id);

// Create a Podcast and attach to Playlist
$podcast = Podcast::create(['title' => 'Tech Talk', 'host' => 'John Doe']);
$playlist->podcasts()->attach($podcast->id);
Output
songs table
id title artist created_at updated_at
1 Shape of You Ed Sheeran 2025-08-28 07:36:16 2025-08-28 07:36:16
videos table
id title channel created_at updated_at
1 Laravel Tutorial Code Academy 2025-08-28 07:36:16 2025-08-28 07:36:16
podcasts table
id title host created_at updated_at
1 Tech Talk John Doe 2025-08-28 07:36:16 2025-08-28 07:36:16
playlists table
id name created_at updated_at
1 My Favorites 2025-08-28 07:36:16 2025-08-28 07:36:16
playlistables table
id playlist_id playlistable_id playlistable_type created_at updated_at
1 1 1 App\Models\Song 2025-08-28 07:36:16 2025-08-28 07:36:16
2 1 1 App\Models\Video 2025-08-28 07:36:16 2025-08-28 07:36:16
3 1 1 App\Models\Podcast 2025-08-28 07:36:16 2025-08-28 07:36:16

Read

use App\Models\Song;
use App\Models\Video;
use App\Models\Podcast;
use App\Models\Playlist;


// Get all Songs in a Playlist
$playlist = Playlist::find(1);
foreach ($playlist->songs as $song) {
    echo $song->title . " by " . $song->artist."<br>";
}

// Get all Videos in a Playlist
$playlist = Playlist::find(1);
foreach ($playlist->videos as $video) {
    echo $video->title . " from " . $video->channel."<br>";
}

// Get all Podcasts in a Playlist
$playlist = Playlist::find(1);
foreach ($playlist->podcasts as $podcast) {
    echo $podcast->title . " hosted by " . $podcast->host."<br>";
}

// Get Playlist from a Song
$song = Song::find(1);
foreach ($song->playlists as $playlist) {
    echo $playlist->name."<br>";
}

Update 

// Update Playlist name
$playlist = Playlist::find(1);
$playlist->update(['name' => 'Updated Playlist']);

// Update Song in Playlist
$song = Song::find(1);
$song->update(['title' => 'Perfect']);

// Sync Songs (Update Attached Songs List)
$playlist = Playlist::find(1);
$playlist->songs()->sync([2, 3]);   // Replace all songs with only songs 2, 3

// Sync Without Detaching (Keep Old Songs + Add New)
$playlist = Playlist::find(1);
$playlist->songs()->syncWithoutDetaching([4, 5]);  // Add songs 4 & 5 without removing existing songs

Delete

// Remove one Song from Playlist
$playlist = Playlist::find(1);
$playlist->songs()->detach(1); // Detach song with id 1

// Remove all Videos from Playlist
$playlist->videos()->detach();

// Delete a Playlist (will not delete songs/videos/podcasts themselves)
$playlist->delete();

Eager loading (optimizes queries)

use App\Models\Song;
use App\Models\Video;
use App\Models\Podcast;
use App\Models\Playlist;


// Get all Songs in a Playlist
$playlist = Playlist::with('songs')->find(1);
foreach ($playlist->songs as $song) {
    echo $song->title . " by " . $song->artist;
}

// Get all Videos in a Playlist
$playlist = Playlist::with('videos')->find(1);
foreach ($playlist->videos as $video) {
    echo $video->title . " from " . $video->channel;
}

// Get all Podcasts in a Playlist
$playlist = Playlist::with('podcasts')->find(1);
foreach ($playlist->podcasts as $podcast) {
    echo $podcast->title . " hosted by " . $podcast->host;
}

// Get Playlist from a Song
$song = Song::with('playlists')->find(1);
foreach ($song->playlists as $playlist) {
    echo $playlist->name;
}

Always use with() when fetching related data in bulk.


Whereisstuff is simple learing platform for beginer to advance level to improve there skills in technologies.we will provide all material free of cost.you can write a code in runkit workspace and we provide some extrac features also, you agree to have read and accepted our terms of use, cookie and privacy policy.
© Copyright 2024 www.whereisstuff.com. All rights reserved. Developed by whereisstuff Tech.