Unified Table v5 Polymorphic

Tek Tablo, Sonsuz Dağıtım

3 pivot tabloyu 1'e indir: playlistables

30 Aralık 2025 En Elegant Çözüm

Fikir: Tüm Dağıtımlar Tek Tabloda

Mevcut Durum (3 Ayrı Tablo)

playlist_sector
playlist_radio
playlist_corporate (yeni)

3 tablo, 3 migration, 3 relation method...

v5 Önerisi (1 Unified Tablo)

playlistables
playlist_id → Playlist
playlistable_id → Target ID
playlistable_type → 'sector' | 'radio' | 'corporate'

1 tablo, 1 migration, polymorphic relation!

Dönüşüm

ÖNCE (3 Ayrı Pivot Tablo):

┌──────────────────────┐  ┌──────────────────────┐  ┌──────────────────────┐
│  playlist_sector     │  │  playlist_radio      │  │  playlist_corporate  │
├──────────────────────┤  ├──────────────────────┤  ├──────────────────────┤
│ playlist_id          │  │ playlist_id          │  │ playlist_id          │
│ sector_id            │  │ radio_id             │  │ corporate_id         │
└──────────────────────┘  └──────────────────────┘  └──────────────────────┘
         │                         │                         │
         └─────────────────────────┼─────────────────────────┘
                                   │
                                   ▼
SONRA (1 Unified Tablo):

                    ┌────────────────────────────────┐
                    │         playlistables          │
                    ├────────────────────────────────┤
                    │ id (PK)                        │
                    │ playlist_id (FK)               │
                    │ playlistable_id                │
                    │ playlistable_type              │  ← 'sector', 'radio', 'corporate'
                    │ created_at (optional)          │
                    └────────────────────────────────┘
                                   │
           ┌───────────────────────┼───────────────────────┐
           ▼                       ▼                       ▼
    ┌─────────────┐         ┌─────────────┐         ┌─────────────┐
    │   Sector    │         │    Radio    │         │  Corporate  │
    └─────────────┘         └─────────────┘         └─────────────┘

Migration

create_playlistables_table.php

Schema::create('playlistables', function (Blueprint $table) {
    $table->id();

    $table->foreignId('playlist_id')
          ->constrained('muzibu_playlists', 'playlist_id')
          ->cascadeOnDelete();

    // Polymorphic columns
    $table->unsignedBigInteger('playlistable_id');
    $table->string('playlistable_type', 50);

    $table->timestamps();

    // Unique constraint: Aynı playlist + aynı target + aynı type = 1 kayıt
    $table->unique(['playlist_id', 'playlistable_id', 'playlistable_type'], 'playlistables_unique');

    // Indexes for fast queries
    $table->index(['playlistable_type', 'playlistable_id'], 'playlistables_morph_idx');
    $table->index('playlist_id');
});

Data Migration (Mevcut Verileri Taşı)

// Mevcut sector verilerini taşı
DB::table('muzibu_playlist_sector')->get()->each(function($row) {
    DB::table('playlistables')->insert([
        'playlist_id' => $row->playlist_id,
        'playlistable_id' => $row->sector_id,
        'playlistable_type' => 'sector',
    ]);
});

// Mevcut radio verilerini taşı
DB::table('muzibu_playlist_radio')->get()->each(function($row) {
    DB::table('playlistables')->insert([
        'playlist_id' => $row->playlist_id,
        'playlistable_id' => $row->radio_id,
        'playlistable_type' => 'radio',
    ]);
});

Model Implementation

Playlist.php

/**
 * Sektörler (polymorphic)
 */
public function sectors()
{
    return $this->morphedByMany(
        Sector::class,
        'playlistable',
        'playlistables',
        'playlist_id',
        'playlistable_id',
        'playlist_id',
        'sector_id'
    );
}

/**
 * Radyolar (polymorphic)
 */
public function radios()
{
    return $this->morphedByMany(
        Radio::class,
        'playlistable',
        'playlistables',
        'playlist_id',
        'playlistable_id',
        'playlist_id',
        'radio_id'
    );
}

/**
 * Kurumlar (polymorphic) - YENİ!
 */
public function corporates()
{
    return $this->morphedByMany(
        MuzibuCorporateAccount::class,
        'playlistable',
        'playlistables',
        'playlist_id',
        'playlistable_id',
        'playlist_id',
        'id'
    );
}

Target Models (Sector, Radio, Corporate)

// Sector.php
public function playlists()
{
    return $this->morphToMany(
        Playlist::class,
        'playlistable',
        'playlistables',
        'playlistable_id',
        'playlist_id',
        'sector_id',
        'playlist_id'
    );
}

// Radio.php - Aynı pattern
public function playlists() { /* ... */ }

// MuzibuCorporateAccount.php - Aynı pattern
public function playlists()
{
    return $this->morphToMany(
        Playlist::class,
        'playlistable',
        'playlistables',
        'playlistable_id',
        'playlist_id',
        'id',
        'playlist_id'
    );
}

Morph Map (AppServiceProvider)

// app/Providers/AppServiceProvider.php → boot()

Relation::morphMap([
    'sector' => Sector::class,
    'radio' => Radio::class,
    'corporate' => MuzibuCorporateAccount::class,
]);

// Bu sayede DB'de 'sector' yazıyor, tam class adı değil
// Daha temiz, daha kısa, daha okunabilir

Kullanım (v4 ile Aynı!)

En güzel yanı: Kullanım syntax'ı değişmiyor!

Playlist'e Kurum Ekle

$playlist->corporates()->attach($id);

Kurumun Playlistleri

$corporate->playlists()->get();

Playlist'in Sektörleri

$playlist->sectors()->get();

Sync Multiple

$playlist->corporates()->sync([1, 5, 12]);

v4 vs v5 Karşılaştırma

v4: Ayrı Tablo (Mevcut Pattern)

Mevcut kodu değiştirmeye gerek yok
Data migration yok
Risk düşük
3 ayrı tablo = daha fazla bakım
Gelecekte yeni tip eklenirse +1 tablo
En uygun: Hızlı çözüm, minimum risk

v5: Unified Table (Polymorphic)

Tek tablo = tek bakım noktası
Gelecekte yeni tip = sadece morph map
Daha elegant, Laravel best practice
Mevcut relation'ları değiştirmek gerekir
Data migration gerekli
En uygun: Uzun vadeli, temiz mimari

Alternatif: Hibrit Yaklaşım

Mevcut tabloları koru, yeni için unified

Eğer mevcut sector/radio tablolarını değiştirmek istemiyorsan:

  • playlist_sector → Olduğu gibi kalsın
  • playlist_radio → Olduğu gibi kalsın
  • playlist_corporate → v4 pattern ile ekle
  • Gelecekte gerekirse → Unified'a migrate et
Bu "pragmatik" yaklaşım: Şimdi çalışan şeyi bozmadan ilerle, gerekirse refactor

Karar Matrisi

Öncelik v4 Seç v5 Seç
Hızlı teslim
Minimum risk
Uzun vadeli bakım
Gelecekte yeni tipler
Elegant mimari
Test edilmiş pattern
🤔

Sen Karar Ver

🚀

v4: Şimdi Çalışsın

Mevcut pattern, yeni pivot tablo
Hızlı, güvenli, test edilmiş

🏗️

v5: Geleceğe Yatırım

Unified tablo, polymorphic
Temiz mimari, ölçeklenebilir

Tavsiyem: v4 ile başla, çalışsın. İleride gerekirse v5'e migrate et.