Admin Paneli Geliştirme - Analiz Raporu v2 (Güncellenmiş)
İ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.
Gerçek: 62 şarkı, 15722 saniye Cache: 62 şarkı, 15950 saniye (228 saniye fazla)
// Normal attach() + tek seferde recalculate $playlist->songs()->attach($songsToAttach); $playlist->recalculateCachedCounts();
Ş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.
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
Song modelinde forceDeleting event listener eklendi:
1. Pivot kayıtlarını temizle 2. Etkilenen playlist'leri bul 3. Cache'lerini yeniden hesapla
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.
Şarkı dosyaları kopyalanmayacak (sunucuda yer kaplamaz). Sadece playlist içindeki şarkı listesi (referanslar) kopyalanacak. Yani iki playlist de aynı şarkı dosyalarını kullanır.
Modules/Muzibu/App/Services/PlaylistService.php
duplicatePlaylist(int $playlistId): MuzibuOperationResult
Modules/Muzibu/App/Http/Livewire/Admin/PlaylistComponent.php
duplicatePlaylist(int $id): void
Modules/Muzibu/resources/views/admin/livewire/playlist-component.blade.php
Modules/Muzibu/App/Models/Song.php
booted() metodu - forceDeleting event listener
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)
| 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 |
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(), ]); }); }
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'); } }