Laravel İle Dinamik Bir İçerik Versiyonlama Sistemi Nasıl Yapılır?

Laravel İle Dinamik Bir İçerik Versiyonlama Sistemi Nasıl Yapılır?
Laravel İle Dinamik Bir İçerik Versiyonlama Sistemi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Bu sistemi kurabilmek için Laravel 11 veya üzeri bir sürümün kurulu olduğu bir projeye ihtiyacınız vardır. Veritabanı işlemleri için MySQL veya PostgreSQL kullanılması önerilir. Ayrıca, veritabanı şemalarını yönetmek için Laravel Migration (Göç) sistemine hakim olmanız gerekmektedir.

  • PHP 8.2+ sürümü.
  • Laravel 11.x framework.
  • Composer paket yöneticisi.
  • Temel veritabanı ilişkileri (One-to-Many) bilgisi.

Adım 1: Veritabanı Şemasını Tasarlama

Versiyonlama sistemi için ana tablomuzun yanı sıra, bu tablodaki değişiklikleri tutacak bir contents_versions tablosuna ihtiyacımız var. Bu tablo, hangi modelin, hangi verisinin, ne zaman değiştiğini saklayacaktır.


// 2026_01_01_000000_create_content_versions_table.php
Schema::create('content_versions', function (Blueprint $table) {
    $table->id();
    $table->morphs('versionable'); // Hangi modelin versiyonu olduğunu tutar
    $table->json('data'); // İçeriğin o anki hali
    $table->unsignedBigInteger('user_id')->nullable(); // Değişikliği yapan kullanıcı
    $table->timestamps();
});

Burada kullanılan morphs yöntemi, sistemin sadece bir tabloya değil, projenizdeki herhangi bir modele (Post, Page, Product vb.) esnek bir şekilde bağlanmasını sağlar.

Adım 2: Versiyonlanabilir Trait Oluşturma

Kod tekrarını önlemek ve temiz bir mimari sunmak için versiyonlama mantığını bir Trait (Özellik) içerisinde tanımlamalıyız. Bu sayede, istediğiniz modele tek satırlık bir kodla versiyonlama özelliği kazandırabilirsiniz.


namespace App\Traits;

use App\Models\ContentVersion;

trait Versionable {
    public function versions() {
        return $this->morphMany(ContentVersion::class, 'versionable');
    }

    public function saveVersion() {
        $this->versions()->create([
            'data' => $this->getAttributes(),
            'user_id' => auth()->id(),
        ]);
    }
}

Bu Trait, modelin versions ilişkisini kurar ve saveVersion metodu ile o anki veritabanı satırını JSON formatında yedekler.

Adım 3: Model Entegrasyonu ve Uygulama

Şimdi bu özelliği bir model üzerinde kullanalım. Örneğin, bir Post modeliniz olduğunu varsayalım. Trait'i modele dahil etmek oldukça basittir.


namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Traits\Versionable;

class Post extends Model {
    use Versionable;

    protected $fillable = ['title', 'body', 'status'];
}

Modelinize Versionable trait'ini eklediğinizde, artık $post->saveVersion() komutunu kullanarak istediğiniz zaman içeriğin bir kopyasını alabilirsiniz.

Adım 4: Versiyon Geri Yükleme Mantığı

Sistemin en önemli parçası, geçmiş bir versiyona geri dönmektir. Bunun için ContentVersion modeline bir metot ekleyerek veritabanını güncelleyebiliriz.


// ContentVersion.php modeli içerisinde
public function restore() {
    $model = $this->versionable;
    $model->fill($this->data);
    return $model->save();
}

Bu yöntem, JSON verisini modelin özelliklerine (attributes) doldurur ve save() metodu ile veritabanına yazar. İşlem sırasında fillable dizisine dikkat etmeniz, güvenlik açısından önemlidir.

Karşılaştırma: Versiyonlama Yöntemleri

Yöntem Avantaj Dezavantaj
JSON Snapshot (Bizim yöntemimiz) Hızlı sorgulama, kolay entegrasyon Veritabanı boyutu hızlı artar
Audit Log Paketi Hazır çözüm, kapsamlı Özelleştirme zorluğu
Sütun Bazlı Versiyonlama Detaylı fark takibi Karmaşık veritabanı yapısı

Adım 5: Güvenlik ve Performans İpuçları

Büyük ölçekli uygulamalarda versiyonlama tabloları hızla büyüyebilir. Bu durum veritabanı performansını etkiler. Ayrıca, kullanıcıların verileri manipüle etmesini engellemek için yetkilendirme katmanını (Policy) kullanmalısınız.

Kritik Uyarı: Versiyonlama tablosuna veri yazarken SQL Injection riskine karşı her zaman Laravel'in Eloquent ORM yapısını kullanın. Asla doğrudan ham SQL sorguları ile kullanıcıdan gelen veriyi işlemeyin. Ayrıca, hassas verileri (şifre, API anahtarı vb.) versiyonlama tablosuna kaydetmekten kaçının.

Sıkça Sorulan Sorular

Versiyonlama tablosu çok büyürse ne yapmalıyım?

Belirli bir süreden eski olan versiyonları silen veya bunları soğuk depolama (S3 gibi) alanlarına taşıyan bir "Pruning" (budama) komutu yazarak veritabanınızı optimize edebilirsiniz.

Sadece değişen sütunları kaydedebilir miyim?

Evet, $this->getChanges() metodunu kullanarak sadece değişen alanları JSON olarak saklayabilirsiniz. Bu, veritabanı tasarrufu sağlar.

Versiyonlar arası farkı nasıl gösteririm?

Laravel'de getDiff() benzeri mantıklar kurabilir veya sebastian/diff kütüphanesini kullanarak iki JSON verisi arasındaki farkı kullanıcıya görsel olarak sunabilirsiniz.

Bu sistem API ile çalışır mı?

Evet, modelinizde bu Trait'i kullandığınız sürece, API controller içerisinde $model->saveVersion() çağrısı yaparak her API güncellemesinde versiyon alabilirsiniz.

Versiyonları kimin oluşturduğunu nasıl takip ederim?

Trait içerisindeki user_id sütununu, o anki oturumu açmış kullanıcıya (auth()->id()) bağlayarak tam bir denetim izi oluşturabilirsiniz.

Versiyonlama Sisteminde Birim Testleri (Unit Testing)

Dinamik bir versiyonlama sistemi kurduğunuzda, verilerin doğru şekilde yedeklendiğinden ve geri yüklendiğinden emin olmak için test süreçlerini otomatize etmeniz kritik önem taşır. Laravel'in Pest veya PHPUnit kütüphanesini kullanarak, versiyonlama mantığınızın doğruluğunu şu şekilde test edebilirsiniz:

public function test_content_version_is_created_on_update()
{
    $post = Post::factory()->create(['title' => 'İlk Başlık']);
    
    $post->update(['title' => 'İkinci Başlık']);
    
    $this->assertDatabaseHas('content_versions', [
        'versionable_id' => $post->id,
        'versionable_type' => Post::class,
        'payload' => json_encode(['title' => 'İlk Başlık'])
    ]);
}

Bu test, model üzerinde bir güncelleme yapıldığında, eski verinin başarıyla content_versions tablosuna aktarılıp aktarılmadığını doğrular. Özellikle kritik verilerle çalışıyorsanız, restore metodunuzun da veritabanı durumunu eski haline getirdiğini kontrol eden bir test yazmanız önerilir.

Sistem Performansını Optimize Etme Stratejileri

Versiyonlama tabloları, yoğun trafik alan uygulamalarda kısa sürede milyonlarca satıra ulaşabilir. Bu durum, veritabanı sorgularının yavaşlamasına neden olur. Performansı korumak için aşağıdaki stratejileri uygulayabilirsiniz:

  • Partitioning (Bölümleme): MySQL veya PostgreSQL üzerinde tarih bazlı tablo bölümleme kullanarak, eski versiyonların sorgulanmasını hızlandırabilirsiniz.
  • Asenkron İşleme: Eğer versiyonlama süreci kullanıcı deneyimini (yavaş yanıt süresi) etkiliyorsa, versiyon kaydetme işlemini Laravel Job yapısı ile arka plana atabilirsiniz.
  • Veri Sıkıştırma: JSON payload verilerini veritabanına kaydetmeden önce gzcompress() fonksiyonu ile sıkıştırıp, okurken gzuncompress() ile açarak disk kullanımını %70'e varan oranlarda azaltabilirsiniz.
// Versiyon kaydederken sıkıştırma örneği
public function saveVersion($data)
{
    $compressedData = base64_encode(gzcompress(json_encode($data)));
    
    $this->versions()->create([
        'payload' => $compressedData,
        'user_id' => auth()->id()
    ]);
}

// Versiyonu okurken açma örneği
public function getPayloadAttribute($value)
{
    return json_decode(gzuncompress(base64_decode($value)), true);
}

İleri Düzey İpucu: Versiyon Temizleme (Pruning)

Tüm versiyonları sonsuza kadar saklamak yerine, belirli bir süreden eski veya belirli bir sayıdan fazla olan versiyonları otomatik olarak silen bir Scheduled Task tanımlayın:

// app/Console/Kernel.php içerisinde
$schedule->call(function () {
    // 30 günden eski versiyonları temizle
    ContentVersion::where('created_at', '


Sonuç

Laravel ile dinamik bir içerik versiyonlama sistemi kurmak, uygulamanızın güvenilirliğini ve veri yönetimi kalitesini önemli ölçüde artırır. Bu rehberde, Trait yapısını kullanarak modüler bir çözüm geliştirdik. Bir sonraki adım olarak, versiyonlar arasında "Fark Göster" (Diff) arayüzü oluşturarak kullanıcı deneyimini zenginleştirebilirsiniz.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce, veritabanı yükü ve güvenlik politikalarınızı projenizin özel ihtiyaçlarına göre test etmeyi unutmayın.
Bu yazıya tepkinizi paylaşın:
Kerem Tekin

Hobi ve kişisel gelişim alanında içerik editörlüğü yapıyorum. Okuyuculara yeni beceriler kazandıran, net ve anlaşılır nasıl yapılır makaleleri yazıyorum.

Yorumlar (0)

Yorum Yaz