PLANLAMA RAPORU

Muzibu Modülü
Schema.org Implementation

Google Rich Results Uyumlu Yapılandırılmış Veri Planlaması

Tarih: 9 Ocak 2026 | Versiyon: v1 | Modül: Muzibu

📋 Özet

📝 Basit Anlatım (Herkes İçin)

Muzibu müzik platformunun tüm içerikleri (şarkılar, albümler, sanatçılar, playlist'ler vb.) Google'ın anlayabileceği özel bir formata dönüştürülecek. Böylece Google arama sonuçlarında müzikleriniz daha güzel görünecek, yıldız puanları gösterilecek ve kullanıcılar şarkılarınızı daha kolay bulacak.

Kullanıcıya Faydası:

  • ✓ Google'da şarkılar yıldızlı görünür
  • ✓ Albüm bilgileri zenginleştirilmiş gösterilir
  • ✓ Sanatçı sayfaları Google'da öne çıkar
  • ✓ Arama sonuçlarında tıklama oranı artar

🔧 Teknik Detaylar (Geliştiriciler İçin)

Blog modülünde kullanılan HasUniversalSchemas trait pattern'i Muzibu modülünün tüm model'lerine uygulanacak. Her model için uygun schema.org type'ları implement edilecek.

Teknik Stack:

  • • HasUniversalSchemas trait
  • • HasSeo trait (mevcut)
  • • JSON-LD format
  • • Schema.org vocabulary
  • • Google Rich Results API

📊 Kapsam

7
Model
9
Schema Type
3
Database Migration

🔍 Mevcut Durum Analizi

✅ Referans Pattern: Blog Modülü

Blog modülü zaten HasUniversalSchemas trait'ini kullanıyor ve başarıyla çalışıyor. Bu pattern Muzibu modülüne adapte edilecek.

Blog.php'deki Kullanım:

use HasUniversalSchemas;

// Database'de mevcut
protected $fillable = ['faq_data', 'howto_data'];
protected $casts = ['faq_data' => 'array', 'howto_data' => 'array'];

// Schema metodları
public function getAllSchemas(): array {
    return [
        'blogposting' => $this->getSchemaMarkup(),
        'breadcrumb' => $this->getBreadcrumbSchema(),
        'faq' => $this->getFaqSchema(),
        'howto' => $this->getHowToSchema(),
    ];
}

📦 Muzibu Model'lerinin Durumu

Model HasSeo HasUniversalSchemas faq/howto Fields Schema Method
Song ✅ Var ❌ Yok ❌ Yok getSeoFallbackSchemaMarkup()
Album ✅ Var ❌ Yok ❌ Yok getSeoFallbackSchemaMarkup()
Artist ✅ Var ❌ Yok ❌ Yok getSeoFallbackSchemaMarkup()
Playlist ✅ Var ❌ Yok ❌ Yok ❌ Yok
Genre ✅ Var ❌ Yok ❌ Yok getSeoFallbackSchemaMarkup()
Sector ❌ Yok ❌ Yok ❌ Yok ❌ Yok
Radio ✅ Var ❌ Yok ❌ Yok getSeoFallbackSchemaMarkup()

Özet:

  • 6/7 model HasSeo trait kullanıyor (temel schema desteği var)
  • 0/7 model HasUniversalSchemas trait kullanıyor
  • 0/7 model'de faq_data/howto_data field'ları yok
  • • Playlist model'inde schema method eksik

🗺️ Schema Type Mapping

Her Muzibu model'i için uygun schema.org type ve özellikler belirlendi.

🎵 Song Model

MusicRecording

Database Fields:

  • • title (JSON multilang)
  • • lyrics (JSON multilang)
  • • duration (integer, seconds)
  • • album_id (FK → albums)
  • • genre_id (FK → genres)
  • • play_count (integer)

Relations:

  • • album() → Album
  • • genre() → Genre
  • • artist() (through album)
  • • playlists() (many-to-many)

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "MusicRecording",
  "name": "Şarkı Başlığı",
  "duration": "PT180S",
  "url": "https://muzibu.com/songs/sarki-basligi",
  "image": "https://cdn.muzibu.com/covers/song.jpg",
  "byArtist": {
    "@type": "MusicGroup",
    "name": "Sanatçı Adı",
    "url": "https://muzibu.com/artists/sanatci-adi"
  },
  "inAlbum": {
    "@type": "MusicAlbum",
    "name": "Albüm Adı",
    "url": "https://muzibu.com/albums/album-adi"
  },
  "genre": "Pop",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.5",
    "reviewCount": 120,
    "bestRating": "5",
    "worstRating": "1"
  }
}

Ek Schema'lar:

  • BreadcrumbList: Ana Sayfa → Şarkılar → Şarkı Detay
  • FAQPage: Şarkı hakkında SSS (opsiyonel)
  • HowTo: Şarkıyı nasıl dinlerim? (opsiyonel)

💿 Album Model

MusicAlbum

Database Fields:

  • • title (JSON multilang)
  • • description (JSON multilang)
  • • artist_id (FK → artists)
  • • songs_count (cached)
  • • total_duration (cached)

Relations:

  • • artist() → Artist
  • • songs() → Song[]

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "MusicAlbum",
  "name": "Albüm Başlığı",
  "description": "Albüm açıklaması",
  "url": "https://muzibu.com/albums/album-basligi",
  "image": "https://cdn.muzibu.com/covers/album.jpg",
  "byArtist": {
    "@type": "MusicGroup",
    "name": "Sanatçı Adı",
    "url": "https://muzibu.com/artists/sanatci-adi"
  },
  "numTracks": 12,
  "track": [
    {
      "@type": "MusicRecording",
      "name": "Şarkı 1",
      "duration": "PT180S",
      "position": 1
    }
  ],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": 85
  }
}

Ek Schema'lar:

  • BreadcrumbList: Ana Sayfa → Albümler → Albüm Detay
  • FAQPage: Albüm hakkında SSS

🎤 Artist Model

MusicGroup / Person

Database Fields:

  • • title (JSON multilang)
  • • bio (JSON multilang)
  • • albums_count (cached)
  • • songs_count (cached)

Relations:

  • • albums() → Album[]
  • • songs() (through albums)

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "MusicGroup",
  "name": "Sanatçı Adı",
  "description": "Sanatçı biyografisi",
  "url": "https://muzibu.com/artists/sanatci-adi",
  "image": "https://cdn.muzibu.com/photos/artist.jpg",
  "genre": ["Pop", "Rock"],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9",
    "reviewCount": 230
  },
  "album": [
    {
      "@type": "MusicAlbum",
      "name": "Albüm 1",
      "url": "https://muzibu.com/albums/album-1"
    }
  ]
}

Ek Schema'lar:

  • BreadcrumbList: Ana Sayfa → Sanatçılar → Sanatçı Detay
  • FAQPage: Sanatçı hakkında SSS

📋 Playlist Model

MusicPlaylist

Database Fields:

  • • title (JSON multilang)
  • • description (JSON multilang)
  • • user_id (FK → users)
  • • is_public (boolean)
  • • songs_count (cached)

Relations:

  • • songs() (many-to-many)
  • • user() → User

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "MusicPlaylist",
  "name": "Playlist Başlığı",
  "description": "Playlist açıklaması",
  "url": "https://muzibu.com/playlists/playlist-basligi",
  "numTracks": 25,
  "track": [
    {
      "@type": "MusicRecording",
      "name": "Şarkı 1",
      "position": 1
    }
  ]
}

Ek Schema'lar:

  • BreadcrumbList: Ana Sayfa → Playlist'ler → Playlist Detay

🎸 Genre Model

DefinedTerm

Database Fields:

  • • title (JSON multilang)
  • • description (JSON multilang)
  • • songs_count (cached)

Relations:

  • • songs() → Song[]
  • • playlists() (polymorphic)

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "DefinedTerm",
  "name": "Pop",
  "description": "Popüler müzik türü",
  "url": "https://muzibu.com/genres/pop",
  "inDefinedTermSet": {
    "@type": "DefinedTermSet",
    "name": "Müzik Türleri"
  }
}

Ek Schema'lar:

  • BreadcrumbList: Ana Sayfa → Türler → Tür Detay
  • CollectionPage: Genre kategorisindeki şarkılar

📻 Radio Model

RadioStation

Database Fields:

  • • title (JSON multilang)

Relations:

  • • sectors() (many-to-many)
  • • playlists() (polymorphic)

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "RadioStation",
  "name": "Radyo Adı",
  "url": "https://muzibu.com/radios/radyo-adi",
  "image": "https://cdn.muzibu.com/logos/radio.jpg",
  "broadcastServiceTier": "free"
}

🏢 Sector Model

DefinedTerm (Custom)

Not: Sector, iş sektörü kategorisi olduğu için müzik ile ilgili değil. Bu model için genel DefinedTerm kullanılacak veya schema implementasyonu opsiyonel olabilir.

Schema.org JSON-LD Örneği:

{
  "@context": "https://schema.org",
  "@type": "DefinedTerm",
  "name": "Otel",
  "description": "Otelcilik sektörü",
  "inDefinedTermSet": {
    "@type": "DefinedTermSet",
    "name": "İş Sektörleri"
  }
}

🚀 Implementation Adımları

1

Database Migration Oluştur

Song, Album, Artist, Playlist model'lerine faq_data ve howto_data field'larını ekle.

Migration Dosya Konumu:

Modules/Muzibu/database/migrations/2026_01_09_add_schema_fields_to_muzibu_tables.php
Modules/Muzibu/database/migrations/tenant/2026_01_09_add_schema_fields_to_muzibu_tables.php

Migration SQL:

// muzibu_songs tablosu
ALTER TABLE muzibu_songs
ADD COLUMN faq_data JSON NULL AFTER lyrics,
ADD COLUMN howto_data JSON NULL AFTER faq_data;

// muzibu_albums tablosu
ALTER TABLE muzibu_albums
ADD COLUMN faq_data JSON NULL AFTER description,
ADD COLUMN howto_data JSON NULL AFTER faq_data;

// muzibu_artists tablosu
ALTER TABLE muzibu_artists
ADD COLUMN faq_data JSON NULL AFTER bio,
ADD COLUMN howto_data JSON NULL AFTER faq_data;

// muzibu_playlists tablosu
ALTER TABLE muzibu_playlists
ADD COLUMN faq_data JSON NULL AFTER description,
ADD COLUMN howto_data JSON NULL AFTER faq_data;

⚠️ Kritik: Migration dosyası hem central hem tenant klasörüne eklenecek!

2

Model'lere HasUniversalSchemas Trait Ekle

Her model'e use HasUniversalSchemas; ekle ve $fillable / $casts array'lerini güncelle.

Örnek: Song.php

use App\Traits\HasUniversalSchemas;

class Song extends BaseModel implements TranslatableEntity, HasMedia
{
    use Sluggable, HasTranslations, HasSeo, HasUniversalSchemas, ...;

    protected $fillable = [
        // ... mevcut field'lar
        'faq_data',
        'howto_data',
    ];

    protected $casts = [
        // ... mevcut cast'lar
        'faq_data' => 'array',
        'howto_data' => 'array',
    ];
}

Uygulanacak Model'ler:

  • ✓ Song.php
  • ✓ Album.php
  • ✓ Artist.php
  • ✓ Playlist.php
  • ⚠️ Genre.php (opsiyonel - kategori sayfası)
  • ⚠️ Radio.php (opsiyonel - detay sayfası yok)
  • ❌ Sector.php (gerek yok)
3

Schema Method'ları Implement Et

Her model'de getAllSchemas() ve getBreadcrumbSchema() method'larını override et.

Örnek: Song.php

/**
 * Tüm schema'ları al (MusicRecording + Universal schemas)
 */
public function getAllSchemas(): array
{
    $schemas = [];

    // 1. MusicRecording Schema (Ana içerik)
    $songSchema = $this->getSchemaMarkup();
    if ($songSchema) {
        $schemas['musicrecording'] = $songSchema;
    }

    // 2. Breadcrumb Schema
    $breadcrumbSchema = $this->getBreadcrumbSchema();
    if ($breadcrumbSchema) {
        $schemas['breadcrumb'] = $breadcrumbSchema;
    }

    // 3. FAQ Schema (varsa)
    $faqSchema = $this->getFaqSchema();
    if ($faqSchema) {
        $schemas['faq'] = $faqSchema;
    }

    // 4. HowTo Schema (varsa)
    $howtoSchema = $this->getHowToSchema();
    if ($howtoSchema) {
        $schemas['howto'] = $howtoSchema;
    }

    return $schemas;
}

/**
 * Generate BreadcrumbList Schema for Song
 * Override from HasUniversalSchemas trait
 */
public function getBreadcrumbSchema(): ?array
{
    $locale = app()->getLocale();
    $breadcrumbs = [];
    $position = 1;

    // 1. Home
    $breadcrumbs[] = [
        '@type' => 'ListItem',
        'position' => $position++,
        'name' => __('Ana Sayfa'),
        'item' => url('/')
    ];

    // 2. Şarkılar Ana Sayfa
    $breadcrumbs[] = [
        '@type' => 'ListItem',
        'position' => $position++,
        'name' => __('Şarkılar'),
        'item' => url('/songs')
    ];

    // 3. Current Song
    $breadcrumbs[] = [
        '@type' => 'ListItem',
        'position' => $position,
        'name' => $this->getTranslated('title', $locale),
        'item' => $this->getUrl($locale)
    ];

    return [
        '@context' => 'https://schema.org',
        '@type' => 'BreadcrumbList',
        'itemListElement' => $breadcrumbs
    ];
}

Her Model İçin:

  • • Song: getAllSchemas() + getBreadcrumbSchema()
  • • Album: getAllSchemas() + getBreadcrumbSchema()
  • • Artist: getAllSchemas() + getBreadcrumbSchema()
  • • Playlist: getAllSchemas() + getBreadcrumbSchema()
  • • Genre: getAllSchemas() + getBreadcrumbSchema()
4

View'lerde Schema JSON-LD Render Et

Her detay sayfasında (show.blade.php) <head> içinde schema JSON-LD'yi ekle.

Örnek: songs/show.blade.php

@section('head')
    @parent

    {{-- Schema.org JSON-LD --}}
    @php
        $schemas = $song->getAllSchemas();
    @endphp

    @foreach($schemas as $key => $schema)
        
    @endforeach
@endsection

Güncellenecek View Dosyaları:

  • • Modules/Muzibu/resources/views/songs/show.blade.php
  • • Modules/Muzibu/resources/views/albums/show.blade.php
  • • Modules/Muzibu/resources/views/artists/show.blade.php
  • • Modules/Muzibu/resources/views/playlists/show.blade.php
  • • Modules/Muzibu/resources/views/genres/show.blade.php
5

Google Rich Results Test

Her sayfa için Google Rich Results Test ile doğrula.

Test Edilecek Sayfalar:

  • ✓ https://muzibu.com/songs/ornek-sarki
  • ✓ https://muzibu.com/albums/ornek-album
  • ✓ https://muzibu.com/artists/ornek-sanatci
  • ✓ https://muzibu.com/playlists/ornek-playlist
  • ✓ https://muzibu.com/genres/pop

Beklenen Sonuçlar:

  • ✓ MusicRecording schema valid
  • ✓ MusicAlbum schema valid
  • ✓ MusicGroup schema valid
  • ✓ BreadcrumbList schema valid
  • ✓ FAQPage schema valid (varsa)
  • ✓ AggregateRating görünür

💻 Migration Kod Örneği

Modules/Muzibu/database/migrations/2026_01_09_add_schema_fields_to_muzibu_tables.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        // muzibu_songs tablosu
        if (Schema::hasTable('muzibu_songs')) {
            Schema::table('muzibu_songs', function (Blueprint $table) {
                if (!Schema::hasColumn('muzibu_songs', 'faq_data')) {
                    $table->json('faq_data')->nullable()->after('lyrics');
                }
                if (!Schema::hasColumn('muzibu_songs', 'howto_data')) {
                    $table->json('howto_data')->nullable()->after('faq_data');
                }
            });
        }

        // muzibu_albums tablosu
        if (Schema::hasTable('muzibu_albums')) {
            Schema::table('muzibu_albums', function (Blueprint $table) {
                if (!Schema::hasColumn('muzibu_albums', 'faq_data')) {
                    $table->json('faq_data')->nullable()->after('description');
                }
                if (!Schema::hasColumn('muzibu_albums', 'howto_data')) {
                    $table->json('howto_data')->nullable()->after('faq_data');
                }
            });
        }

        // muzibu_artists tablosu
        if (Schema::hasTable('muzibu_artists')) {
            Schema::table('muzibu_artists', function (Blueprint $table) {
                if (!Schema::hasColumn('muzibu_artists', 'faq_data')) {
                    $table->json('faq_data')->nullable()->after('bio');
                }
                if (!Schema::hasColumn('muzibu_artists', 'howto_data')) {
                    $table->json('howto_data')->nullable()->after('faq_data');
                }
            });
        }

        // muzibu_playlists tablosu
        if (Schema::hasTable('muzibu_playlists')) {
            Schema::table('muzibu_playlists', function (Blueprint $table) {
                if (!Schema::hasColumn('muzibu_playlists', 'faq_data')) {
                    $table->json('faq_data')->nullable()->after('description');
                }
                if (!Schema::hasColumn('muzibu_playlists', 'howto_data')) {
                    $table->json('howto_data')->nullable()->after('faq_data');
                }
            });
        }
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        if (Schema::hasTable('muzibu_songs')) {
            Schema::table('muzibu_songs', function (Blueprint $table) {
                $table->dropColumn(['faq_data', 'howto_data']);
            });
        }

        if (Schema::hasTable('muzibu_albums')) {
            Schema::table('muzibu_albums', function (Blueprint $table) {
                $table->dropColumn(['faq_data', 'howto_data']);
            });
        }

        if (Schema::hasTable('muzibu_artists')) {
            Schema::table('muzibu_artists', function (Blueprint $table) {
                $table->dropColumn(['faq_data', 'howto_data']);
            });
        }

        if (Schema::hasTable('muzibu_playlists')) {
            Schema::table('muzibu_playlists', function (Blueprint $table) {
                $table->dropColumn(['faq_data', 'howto_data']);
            });
        }
    }
};

⚠️ Kritik Hatırlatma:

  • • Bu dosyayı hem migrations/ hem de migrations/tenant/ klasörüne kopyala!
  • • Central migration: php artisan migrate --force
  • • Tenant migration: php artisan tenants:migrate --force

✅ Test Checklist

📝 Migration Test

  • Migration dosyaları hem central hem tenant klasöründe oluşturuldu
  • Central migration başarıyla çalıştırıldı
  • Tenant migration başarıyla çalıştırıldı
  • Database'de faq_data ve howto_data field'ları mevcut

🔧 Model Test

  • Song model'e HasUniversalSchemas trait eklendi
  • Album model'e HasUniversalSchemas trait eklendi
  • Artist model'e HasUniversalSchemas trait eklendi
  • Playlist model'e HasUniversalSchemas trait eklendi
  • $fillable ve $casts array'leri güncellendi

📐 Schema Method Test

  • Song: getAllSchemas() ve getBreadcrumbSchema() implement edildi
  • Album: getAllSchemas() ve getBreadcrumbSchema() implement edildi
  • Artist: getAllSchemas() ve getBreadcrumbSchema() implement edildi
  • Playlist: getAllSchemas() ve getBreadcrumbSchema() implement edildi

🎨 View Test

  • songs/show.blade.php'de schema JSON-LD eklendi
  • albums/show.blade.php'de schema JSON-LD eklendi
  • artists/show.blade.php'de schema JSON-LD eklendi
  • playlists/show.blade.php'de schema JSON-LD eklendi

🌐 Google Rich Results Test

  • Song detay sayfası test edildi (MusicRecording schema valid)
  • Album detay sayfası test edildi (MusicAlbum schema valid)
  • Artist detay sayfası test edildi (MusicGroup schema valid)
  • Playlist detay sayfası test edildi (MusicPlaylist schema valid)
  • BreadcrumbList schema tüm sayfalarda valid
  • AggregateRating (yıldızlar) görünüyor

⚠️ Riskler ve Dikkat Edilecek Noktalar

🔴 Kritik Riskler

  • Migration'lar hem central hem tenant klasöründe olmalı!

    Unutulursa sistem çöker. Her iki migration'ı da çalıştır.

  • Bu production sistemi! Backup al!

    Migration öncesi database backup zorunlu.

  • JSON field'lar nullable olmalı!

    faq_data ve howto_data opsiyonel, null olabilir.

⚠️ Orta Seviye Riskler

  • Multi-language JSON field desteği

    faq_data ve howto_data JSON field'larında çoklu dil desteği olmalı. HasUniversalSchemas trait bunu otomatik yapar.

  • Performance: Schema hesaplama maliyeti

    getAllSchemas() her sayfa yüklenişinde çalışır. Cache düşün (gelecekte).

💡 Best Practices

  • Test her model için ayrı ayrı yap

    Song implement et → test et → Album'e geç.

  • Google Rich Results Test her zaman güncel değil

    Bazen "hata yok" gösterir ama Google'da görünmeyebilir. Sabırlı ol.

  • faq_data ve howto_data opsiyonel

    Kullanıcı doldurmak istemiyorsa boş bırakabilir, schema yine de çalışır.

🎯 Beklenen Sonuçlar

✅ SEO Kazançları

  • Google arama sonuçlarında şarkılar yıldızlı görünür
  • Albüm bilgileri zenginleştirilmiş (track count, duration)
  • Sanatçı sayfaları Google Knowledge Graph'te çıkabilir
  • Breadcrumb (ekmek kırıntısı) yolu görünür
  • FAQ sayfaları "Sık Sorulan Sorular" olarak Google'da öne çıkar

📈 Kullanıcı Deneyimi

  • Arama sonuçlarında tıklama oranı (CTR) artar
  • Kullanıcılar şarkıları daha kolay bulur
  • Google Assistant ve voice search desteği artar
  • Müzik platformu olarak profesyonel görünüm
  • Google Discover'da öne çıkma şansı artar

📊 Teknik Kazançlar

  • Google Search Console'da Rich Results raporları gelir
  • Schema.org standardına uyum (future-proof)
  • Diğer arama motorları (Bing, Yandex) de destekler
  • API entegrasyonları için yapılandırılmış veri hazır