Playlist Kopyalama Özelliği

Admin Paneli Geliştirme - Analiz Raporu v2 (Güncellenmiş)

Playlist Yönetimi Hazır Çözüm Hızlı İşlem Cache Fix

v2 Güncellemeleri

Song Model Event: Hard delete edildiğinde playlist cache'leri otomatik güncelleniyor
Cache Double-Counting Fix: Batch insert + recalculateCachedCounts kullanımı
Orphan Kayıt Sorunu: Silinmiş şarkıların pivot tablodan otomatik temizlenmesi
Değiştirilecek Dosya
4 Dosya
Service, Component, Blade, Model
Tahmini Süre
~20 dakika
Test dahil
Risk Seviyesi
Düşük
Mevcut kod etkilenmez

Tespit Edilen Sorunlar ve Çözümler

Sorun 1: Cache Double-Counting

İlk implementasyonda attachSongWithCache() metodunu loop içinde kullanıyorduk. Her şarkı eklenirken cache increment yapılıyordu. Ancak ilk erişimde cache NULL olduğu için getCachedCount() tüm şarkıları hesaplıyor ve üzerine incrementler ekleniyor → double-counting.

Örnek:
Gerçek: 62 şarkı, 15722 saniye
Cache: 62 şarkı, 15950 saniye (228 saniye fazla)
✓ Çözüm:
// Normal attach() + tek seferde recalculate
$playlist->songs()->attach($songsToAttach);
$playlist->recalculateCachedCounts();

Sorun 2: Hard Delete'te Cache Güncellenmesi

Şarkılar hard delete edildiğinde pivot tablodaki kayıtlar orphan kalıyor ve playlist cache'leri güncellenmiyordu. Sonuç: Orijinal playlist'te 103 referans ama sadece 62 şarkı var.

Tespit:
Orijinal Playlist (ID: 27):
- Pivot tabloda: 103 şarkı referansı
- Songs tablosunda: 62 şarkı mevcut
- 41 şarkı hard delete (orphan kayıtlar)
- Cache: 4:37:03 (ESKİ)
- Gerçek: 4:22:02
✓ Çözüm:

Song modelinde forceDeleting event listener eklendi:

1. Pivot kayıtlarını temizle
2. Etkilenen playlist'leri bul
3. Cache'lerini yeniden hesapla

Basit Anlatım (Herkes İçin)

Ne İsteniyor?

Admin panelindeki playlist listesine bir "Kopyala" butonu eklenecek. Bu butona tıklandığında, seçili playlist'in tüm şarkılarıyla birlikte bir kopyası oluşturulacak. Kopya playlist'in adı "Kopya - [Orijinal Ad]" şeklinde olacak.

Nasıl Çalışacak?

1
Buton Görünecek: Her playlist satırında, "Düzenle" butonunun yanında "Kopyala" ikonu çıkacak
2
Kopyalama Başlar: Butona tıklandığında sistem arka planda playlist'i kopyalar
3
Yeni Playlist Oluşur: "Kopya - Rock Classics" gibi yeni bir playlist oluşur
4
Şarkılar Kopyalanır: Orijinal playlist'teki tüm şarkılar aynı sırayla yeni playlist'e eklenir
5
Bildirim Gösterilir: "Playlist başarıyla kopyalandı!" mesajı ekranda belirir

Neden Önemli?

  • Zaman Kazandırır: Benzer playlist oluşturmak için sıfırdan başlamaya gerek kalmaz
  • Hata Riskini Azaltır: Var olan playlist'i baz alarak çalışıldığı için sıfır hata
  • Esneklik Sağlar: Kopyayı özelleştirerek farklı versiyonlar oluşturulabilir
  • Cache Doğruluğu: Otomatik cache yönetimi ile her zaman doğru şarkı sayısı ve süre

Önemli Not

Şarkı dosyaları kopyalanmayacak (sunucuda yer kaplamaz). Sadece playlist içindeki şarkı listesi (referanslar) kopyalanacak. Yani iki playlist de aynı şarkı dosyalarını kullanır.

Teknik Detaylar (Geliştiriciler İçin)

Değiştirilecek Dosyalar

1. PlaylistService.php Yeni Metod + Fix
Modules/Muzibu/App/Services/PlaylistService.php
Eklenecek: duplicatePlaylist(int $playlistId): MuzibuOperationResult
Değişiklik: Batch insert + recalculateCachedCounts (double-counting fix)
2. PlaylistComponent.php Livewire
Modules/Muzibu/App/Http/Livewire/Admin/PlaylistComponent.php
Eklenecek: duplicatePlaylist(int $id): void
3. playlist-component.blade.php UI
Modules/Muzibu/resources/views/admin/livewire/playlist-component.blade.php
Eklenecek: Kopyala butonu (Düzenle linkinin yanında)
4. Song.php Cache Fix
Modules/Muzibu/App/Models/Song.php
Eklenecek: booted() metodu - forceDeleting event listener
Amaç: Hard delete edildiğinde playlist cache'lerini otomatik güncelle

Cache Yönetimi (Veritabanı)

Cache Konumu

Cache Redis'te değil, direkt veritabanında tutuluyor:

Tablo: muzibu_playlists
Kolonlar:
  - songs_count     → Şarkı sayısı (integer)
  - total_duration  → Toplam süre (saniye, integer)

Cache Güncelleme Mekanizmaları

Tetikleyici Metod Açıklama
Şarkı Ekleme attachSongWithCache() Cache increment (+1 şarkı, +duration)
Şarkı Çıkarma detachSongWithCache() Cache decrement (-1 şarkı, -duration)
Manuel Güncelleme recalculateCachedCounts() Tüm cache'i sıfırdan hesapla
Hard Delete (YENİ) Song::forceDeleting event Orphan temizlik + cache recalculate

Hard Delete Cache Güncelleme (Song Model)

protected static function booted()
{
    // Hard delete öncesi event
    static::forceDeleting(function ($song) {
        // 1. Bu şarkının olduğu playlist'leri bul
        $playlistIds = DB::table('muzibu_playlist_song')
            ->where('song_id', $song->song_id)
            ->pluck('playlist_id')
            ->unique();

        // 2. Pivot kayıtlarını temizle (orphan kayıt kalmasın)
        DB::table('muzibu_playlist_song')
            ->where('song_id', $song->song_id)
            ->delete();

        // 3. Etkilenen playlist'lerin cache'ini yeniden hesapla
        if ($playlistIds->isNotEmpty()) {
            $playlists = Playlist::whereIn('playlist_id', $playlistIds)->get();
            foreach ($playlists as $playlist) {
                $playlist->recalculateCachedCounts();
            }
        }

        Log::info('Song force deleted, playlist caches updated', [
            'song_id' => $song->song_id,
            'affected_playlists' => $playlistIds->count(),
        ]);
    });
}

Kod Örneği (PlaylistService.php - Güncellenmiş)

public function duplicatePlaylist(int $playlistId): MuzibuOperationResult
{
    try {
        // Orijinal playlist'i bul
        $sourcePlaylist = Playlist::with('songs')->find($playlistId);

        DB::beginTransaction();

        // Çok dilli başlık oluştur
        $newTitle = [];
        foreach ($titleData as $locale => $value) {
            $newTitle[$locale] = "Kopya - " . $value;
        }

        // Yeni playlist oluştur
        $newPlaylist = new Playlist();
        $newPlaylist->title = json_encode($newTitle, JSON_UNESCAPED_UNICODE);
        // ... diğer alanlar ...
        $newPlaylist->save();

        // ✅ FIX: Batch insert + tek seferde cache hesapla
        $songsToAttach = [];
        foreach ($sourcePlaylist->songs as $song) {
            $songsToAttach[$song->song_id] = [
                'position' => $song->pivot->position,
                'created_at' => now(),
                'updated_at' => now(),
            ];
        }

        if (!empty($songsToAttach)) {
            $newPlaylist->songs()->attach($songsToAttach);
        }

        // Cache'i doğru hesapla (double-counting yok)
        $newPlaylist->recalculateCachedCounts();

        DB::commit();

        return new MuzibuOperationResult(true, 'Playlist başarıyla kopyalandı', 'success');
    } catch (\Exception $e) {
        DB::rollBack();
        return new MuzibuOperationResult(false, 'Kopyalama başarısız', 'error');
    }
}

Güvenlik ve Performans

Güvenlik

  • Permission kontrolü (muzibu.create)
  • Database transaction (rollback garantisi)
  • Playlist ID validasyonu
  • SQL Injection koruması (Eloquent)
  • Orphan kayıt önleme (auto-cleanup)

Performans

  • Eager loading (with('songs'))
  • Batch insert (tek sorguda tüm şarkılar)
  • Dosya kopyalama yok (sadece referans)
  • Cache DB'de (Redis overhead yok)
  • Event-based cache (gerçek zamanlı)

Test Senaryoları

✓ Temel Fonksiyon Testi
  • → Boş playlist kopyalama
  • → 1 şarkılı playlist kopyalama
  • → 100+ şarkılı playlist kopyalama
  • → Çok dilli başlık kontrolü
  • → Cache count'ları doğru mu? (songs_count, total_duration)
✓ Hard Delete Testi (YENİ)
  • → Şarkı hard delete → Pivot temizlendi mi?
  • → Playlist cache'i güncellendi mi?
  • → Çoklu playlist'te aynı şarkı → Hepsi güncellendi mi?
  • → Log kaydı tutuldu mu?
✓ Cache Doğruluk Testi
  • → Orijinal: 4:37:03, Kopya: 4:22:02 → DOĞRU (orphan'lar atlandı)
  • → Double-counting yok mu?
  • → İki kez kopyalama → Aynı sonuç mu?

Sonraki Adımlar

Adım 1
Service Metodu
PlaylistService.php'ye duplicatePlaylist() ekle (cache fix ile)
Adım 2
Livewire Metodu
PlaylistComponent.php'ye duplicatePlaylist() ekle
Adım 3
UI Güncellemesi
Blade template'e Kopyala butonunu ekle
Adım 4
Event Listener
Song.php'ye forceDeleting event ekle
23 Şubat 2026 • Muzibu.com • v2 (Güncellenmiş)