🎵

Muzibu Duration & Count Analizi

Toplu özelliklerin anında güncelleme durumu

🔍 Analiz
📅 11 Ocak 2026 🏢 Tenant 1001 (muzibu.com) 🤖 Claude Sonnet 4.5
📝

Basit Anlatım (Herkes İçin)

Sorun: Müzik platformunda albümlerin, sanatçıların, türlerin ve çalma listelerinin kaç şarkı içerdiği ve toplam süreleri saklanıyor. Yeni şarkı eklendiğinde, düzenlendiğinde veya silindiğinde bu sayılar otomatik güncellenmeli.

Örnek: Bir albüme yeni şarkı eklediğinizde, albümün "5 şarkı, 18 dakika" bilgisi otomatik olarak "6 şarkı, 21 dakika" olmalı.

Durum: Sistem KISMEN ÇALIŞIYOR. Bazı durumlar otomatik, bazıları manuel güncelleme gerektiriyor.

⚙️

Teknik Detaylar (Geliştiriciler İçin)

Migration: 2025_12_26_200000_add_cache_count_fields_to_muzibu_tables.php

Trait: HasCachedCounts (Modules/Muzibu/App/Traits/)

Observer'lar: SongObserver, AlbumObserver, PlaylistObserver

Güncelleme Yöntemleri: incrementCachedCount(), decrementCachedCount(), recalculateCachedCounts()

📊 Tablolardaki Duration & Count Field'ları

🎶 muzibu_playlists

Çalma Listeleri

⚠️ Manuel
songs_count (int, nullable)

Playlist'teki aktif şarkı sayısı

total_duration (int, nullable, saniye)

Playlist'teki tüm şarkıların toplam süresi

⚠️ Manuel Güncelleme Gerekli: Playlist'e şarkı eklenirken/çıkarılırken attachSongWithCache(), detachSongWithCache(), syncSongsWithCache() methodları kullanılmalı!

💿 muzibu_albums

Albümler

✅ Otomatik
songs_count (int, nullable)

Albümdeki aktif şarkı sayısı

total_duration (int, nullable, saniye)

Albümdeki tüm şarkıların toplam süresi

✅ Otomatik Güncelleme: SongObserver şarkı eklendiğinde/silindiğinde/güncellendiğinde otomatik olarak album count'larını günceller.

🎸 muzibu_genres

Müzik Türleri

✅ Otomatik
songs_count (int, nullable)

Bu türdeki aktif şarkı sayısı

total_duration (int, nullable, saniye)

Bu türdeki tüm şarkıların toplam süresi

✅ Otomatik Güncelleme: SongObserver şarkı genre_id'si değiştiğinde eski ve yeni genre count'larını otomatik günceller.

🎤 muzibu_artists

Sanatçılar

✅ Otomatik
albums_count (int, nullable)

Sanatçının aktif albüm sayısı

songs_count (int, nullable)

Sanatçının aktif şarkı sayısı (tüm albümlerden)

total_duration (int, nullable, saniye)

Sanatçının tüm şarkılarının toplam süresi

✅ Otomatik Güncelleme: SongObserver ve AlbumObserver sanatçı count'larını otomatik günceller (Album üzerinden hasManyThrough ilişkisi).

📻 muzibu_radios

Radyo İstasyonları

❌ Cache Yok
songs_count (field yok)

Cache edilmiş field yok

total_duration (field yok)

Cache edilmiş field yok

❌ Runtime Hesaplama: Radio model'inde getTotalDuration() methodu var ama DB'de cache field'ı YOK. Her seferinde tüm playlist'lerden hesaplıyor (YAVAŞ!).

🏢 muzibu_sectors

İş Sektörleri

❌ Cache Yok
songs_count (field yok)

Cache edilmiş field yok

total_duration (field yok)

Cache edilmiş field yok

❌ Cache Sistemi Yok: Sector model'inde duration/count cache field'ları eklenmemiş.

👀 Observer (Otomatik Güncelleme) Sistemi

SongObserver

Dosya: Modules/Muzibu/App/Observers/SongObserver.php

Event'ler: created, updated, deleted, restored, forceDeleted

✅ created:
  • Album: songs_count +1, total_duration +duration
  • Genre: songs_count +1, total_duration +duration
  • Artist (via Album): songs_count +1, total_duration +duration
✅ updated:
  • is_active değişirse → count'ları artır/azalt
  • duration değişirse → total_duration'ı güncelle
  • album_id değişirse → eski album'den çıkar, yeni album'e ekle
  • genre_id değişirse → eski genre'den çıkar, yeni genre'ye ekle
✅ deleted/forceDeleted:
  • Album: songs_count -1, total_duration -duration
  • Genre: songs_count -1, total_duration -duration
  • Artist: songs_count -1, total_duration -duration

AlbumObserver

Dosya: Modules/Muzibu/App/Observers/AlbumObserver.php

Event'ler: created, deleted, restored, forceDeleted

✅ created:
  • Artist: albums_count +1
✅ deleted/forceDeleted:
  • Artist: albums_count -1

⚠️ PlaylistObserver

Dosya: Modules/Muzibu/App/Observers/PlaylistObserver.php

Event'ler: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored, forceDeleting, forceDeleted

⚠️ Sorun:

PlaylistObserver sadece cache temizleme yapıyor. Playlist'e şarkı eklendiğinde/çıkarıldığında songs_count ve total_duration OTOMATIK GÜNCELLENMİYOR!

Sebep: Pivot tablo event'leri (attach/detach/sync) Observer'lar tarafından yakalanmıyor. Laravel pivot event'leri emit etmiyor.

Çözüm: Manuel methodlar kullanılmalı:

  • attachSongWithCache($song)
  • detachSongWithCache($song)
  • syncSongsWithCache($songIds)
  • attachManySongsWithCache($songIds)
  • detachManySongsWithCache($songIds)

🔧 Tespit Edilen Sorunlar ve Çözüm Önerileri

1. Playlist Şarkı Ekleme/Çıkarma Otomatik Değil

Sorun: Playlist'e şarkı eklendiğinde/çıkarıldığında songs_count ve total_duration otomatik güncellenmiyor. Manuel methodlar kullanılmak zorunda.

❌ Yanlış Kullanım:
$playlist->songs()->attach($songId);  // Count'lar güncellenMİYOR!
✅ Doğru Kullanım:
$playlist->attachSongWithCache($songId);  // Count'lar güncellenir

Çözüm Önerileri:

  • Seçenek A: Custom Pivot Model oluştur (BelongsToMany withPivot ile) ve Pivot Model'e Observer ekle
  • Seçenek B: Playlist Model'de songs() ilişkisini override et, attach/detach/sync methodlarını intercept et
  • Seçenek C: Her Playlist işleminde manuel methodları kullanmaya devam et (mevcut durum)

2. Radio ve Sector'de Cache Sistemi Yok

Sorun: Radio ve Sector tablolarında songs_count ve total_duration field'ları yok. Her seferinde runtime'da hesaplama yapılıyor (YAVAŞ!).

Mevcut Durum:
// Radio Model - Her çağrıda playlists'leri foreach ile dolaşıyor
public function getTotalDuration(): int {
    $totalSeconds = 0;
    foreach ($this->playlists as $playlist) {
        $totalSeconds += $playlist->getTotalDuration();
    }
    return $totalSeconds;
}

Çözüm Önerisi:

  • Radio ve Sector tablolarına da songs_count ve total_duration field'larını ekle (migration)
  • HasCachedCounts trait'ini Radio ve Sector modellerine ekle
  • RadioObserver ve SectorObserver'lara cache güncelleme mantığı ekle
  • Playlist değişikliklerinde Radio/Sector count'larını da güncelle
⚠️

3. Bulk İşlemlerde Performance Sorunu

Sorun: attachManySongsWithCache ve detachManySongsWithCache methodları her şarkı için ayrı ayrı hesaplama yapıyor. 100 şarkı eklenirse 100 kez query çalışır.

Mevcut Kod:
// Her şarkı için ayrı query
$addedSongs = Song::whereIn('song_id', $newSongIds)
    ->where('is_active', true)
    ->get();
$addedCount = $addedSongs->count();
$addedDuration = (int) $addedSongs->sum('duration');

Çözüm Önerisi:

  • Bulk işlemlerde tek query ile sum ve count hesapla
  • Transaction kullan (başarısızlıkta rollback)
  • Queue job ile asenkron güncelleme (çok fazla şarkı varsa)

💻 Kullanım Senaryoları

1. Yeni Şarkı Ekleme

// Şarkı oluştur
$song = Song::create([
    'album_id' => 5,
    'genre_id' => 3,
    'title' => ['tr' => 'Yeni Şarkı'],
    'duration' => 240, // 4 dakika
    'is_active' => true,
]);

// ✅ OTOMATIK: SongObserver tetiklenir
// - Album 5: songs_count +1, total_duration +240
// - Genre 3: songs_count +1, total_duration +240
// - Artist (Album 5'in sahibi): songs_count +1, total_duration +240

2. Playlist'e Şarkı Ekleme

❌ YANLIŞ (Count güncellenMİYOR):
$playlist = Playlist::find(10);
$playlist->songs()->attach($songId);  // Count'lar GÜNCELLENMİYOR!
✅ DOĞRU (Count güncellenir):
$playlist = Playlist::find(10);
$playlist->attachSongWithCache($songId);  // songs_count ve total_duration güncellenir

3. Şarkı Süresi Değiştirme

$song = Song::find(25);
$oldDuration = $song->duration; // 240 saniye
$song->duration = 300; // 5 dakika
$song->save();

// ✅ OTOMATIK: SongObserver tetiklenir
// Diff = 300 - 240 = +60 saniye
// - Album: total_duration +60
// - Genre: total_duration +60
// - Artist: total_duration +60

4. Şarkıyı Farklı Albüme Taşıma

$song = Song::find(25);
$oldAlbumId = $song->album_id; // 5
$song->album_id = 8; // Yeni albüm
$song->save();

// ✅ OTOMATIK: SongObserver handleAlbumChange() tetiklenir
// Eski Album 5:
//   - songs_count -1
//   - total_duration -240
// Yeni Album 8:
//   - songs_count +1
//   - total_duration +240
// Eski ve yeni Artist'ler de güncellenir!

5. Toplu Cache Yenileme (Null Olanlar)

// Eğer cache field'ları NULL ise (henüz hesaplanmamışsa)
$album = Album::find(5);
if ($album->needsCachedCountRecalculation()) {
    $values = $album->recalculateCachedCounts();
    // ['songs_count' => 12, 'total_duration' => 2940]
}

📁 İlgili Dosyalar

Migration: Modules/Muzibu/database/migrations/tenant/2025_12_26_200000_add_cache_count_fields_to_muzibu_tables.php
Trait: Modules/Muzibu/App/Traits/HasCachedCounts.php
Model'ler: Modules/Muzibu/App/Models/Playlist.php Modules/Muzibu/App/Models/Album.php Modules/Muzibu/App/Models/Genre.php Modules/Muzibu/App/Models/Artist.php Modules/Muzibu/App/Models/Song.php Modules/Muzibu/App/Models/Radio.php
Observer'lar: Modules/Muzibu/App/Observers/SongObserver.php Modules/Muzibu/App/Observers/AlbumObserver.php Modules/Muzibu/App/Observers/PlaylistObserver.php