Gereksinimler ve Ön Hazırlık
Projeye başlamadan önce sisteminizde Laravel 11 veya üzeri bir sürümün yüklü olduğundan emin olmalısınız. PHP 8.3 ve üzeri sürümler, performans ve güvenlik açısından önerilmektedir. Ayrıca, veritabanı yönetimi için MySQL veya PostgreSQL kullanmanız gerekecektir.
- PHP 8.3+ yüklü bir geliştirme ortamı.
- Composer paket yöneticisi.
- Laravel CLI (Laravel Installer).
- Veritabanı bağlantısı için yapılandırılmış .env dosyası.
Veritabanı Tasarımı ve Migration İşlemleri
Dinamik bir oylama ve inceleme sistemi için "Reviews" (İncelemeler) tablosuna ihtiyacımız var. Bu tablo, kullanıcı ve hedef içerik (örneğin Ürünler) arasında bir köprü görevi görecektir. Veritabanı tasarımında tutarlılık sağlamak için "Foreign Key" (yabancı anahtar) kısıtlamalarını kullanacağız.
Aşağıdaki komutla inceleme tablosunu oluşturacak migration dosyasını hazırlayalım:
php artisan make:migration create_reviews_table
Migration dosyasını açarak gerekli sütunları tanımlayalım. Güvenlik ve veri bütünlüğü için "user_id" ve "product_id" alanlarını index'liyoruz.
public function up(): void
{
Schema::create('reviews', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->text('comment');
$table->unsignedTinyInteger('rating')->comment('1 ile 5 arası puan');
$table->timestamps();
});
}
Bu yapı, bir kullanıcı bir ürünü sildiğinde veya bir kullanıcı silindiğinde, ona bağlı incelemelerin otomatik olarak temizlenmesini sağlar (cascade delete).
Eloquent İlişkilerinin Kurulması
Laravel'in güçlü yönü olan Eloquent ORM (Object-Relational Mapping) ile modeller arasındaki ilişkiyi tanımlamalıyız. User ve Product modellerine inceleme ilişkilerini ekleyeceğiz.
User modelinde incelemeleri şu şekilde tanımlayabilirsiniz:
// App\Models\User.php
public function reviews()
{
return $this->hasMany(Review::class);
}
Product modelinde ise incelemeleri ve ortalama puanı hesaplamak için ilişkiyi kuruyoruz:
// App\Models\Product.php
public function reviews()
{
return $this->hasMany(Review::class);
}
public function averageRating()
{
return $this->reviews()->avg('rating');
}
İnceleme Gönderme İçin Controller Yazımı
Kullanıcıdan gelen veriyi doğrulamak (validation) güvenlik için şarttır. XSS (Cross-Site Scripting) saldırılarını önlemek için kullanıcı girdilerini her zaman temizlemeli ve validate etmeliyiz.
// App\Http\Controllers\ReviewController.php
public function store(Request $request)
{
$validated = $request->validate([
'product_id' => 'required|exists:products,id',
'comment' => 'required|string|max:1000',
'rating' => 'required|integer|min:1|max:5',
]);
$request->user()->reviews()->create($validated);
return back()->with('success', 'İncelemeniz başarıyla kaydedildi.');
}
Burada $request->user() kullanarak, giriş yapmış kullanıcıyı otomatik olarak inceleme ile ilişkilendiriyoruz. Bu, form üzerinden gönderilebilecek sahte user_id değerlerini engeller.
Dinamik Oylama Sistemi ve Frontend Entegrasyonu
Kullanıcıların sayfayı yenilemeden puan vermesini sağlamak için Laravel Livewire veya basit bir JavaScript (Fetch API) kullanabilirsiniz. Aşağıda Blade şablonunda puanlama formunun nasıl görüneceğine dair bir örnek yer almaktadır.
@csrf
@for($i = 1; $i
İleri Seviye Optimizasyon: İnceleme ve Oylama Performansı
Sisteminiz büyüdükçe, özellikle ürün başına yüzlerce veya binlerce inceleme biriktiğinde, veritabanı sorgularınızın performansı kritik bir hale gelir. Laravel'in Eager Loading (istekli yükleme) özelliğini kullanmazsanız, her bir inceleme için kullanıcı bilgilerini çekmeye çalışırken "N+1" sorunuyla karşılaşırsınız. Bu durumu engellemek için Controller tarafında sorgularınızı optimize etmelisiniz.
// Controller içerisinde optimize edilmiş sorgu
public function show(Product $product)
{
$reviews = $product->reviews()
->with('user') // Kullanıcı bilgilerini tek seferde çeker
->latest()
->paginate(10);
return view('products.show', compact('product', 'reviews'));
}
Ayrıca, veritabanı seviyesinde reviews tablosundaki product_id ve user_id sütunlarına mutlaka index eklemelisiniz. Bu, özellikle büyük veri setlerinde arama ve filtreleme hızını ciddi oranda artıracaktır. Migration dosyanızda şu değişikliği yapabilirsiniz:
Schema::table('reviews', function (Blueprint $table) {
$table->index('product_id');
$table->index('created_at'); // Sıralama performansı için
});
İnceleme Sistemi İçin Otomatik Test Süreçleri
Dinamik bir sistemde kullanıcıların hatalı veya kötü niyetli veri göndermesini engellemek için PHPUnit ile testler yazmak, uygulamanızın güvenilirliğini artırır. Bir kullanıcının aynı ürüne mükerrer inceleme yapıp yapmadığını kontrol eden bir test senaryosu şu şekilde kurgulanabilir:
public function test_user_can_only_review_product_once()
{
$user = User::factory()->create();
$product = Product::factory()->create();
// İlk inceleme
$this->actingAs($user)->post('/reviews', [
'product_id' => $product->id,
'comment' => 'Harika bir ürün!',
'rating' => 5
]);
// İkinci inceleme denemesi
$response = $this->actingAs($user)->post('/reviews', [
'product_id' => $product->id,
'comment' => 'Tekrar deniyorum.',
'rating' => 4
]);
$response->assertStatus(422); // Doğrulama hatası dönmeli
}
Bu test, uygulamanızın mantıksal bütünlüğünü korur ve gelecekte yapacağınız güncellemelerde sistemin kırılmasını engeller. Testlerinizi php artisan test komutu ile düzenli olarak çalıştırmak, canlıya almadan önce oluşabilecek hataları minimize eder.
Ölçeklenebilirlik İçin İpucu: Cache Kullanımı
Ürünlerin ortalama puanlarını her seferinde veritabanından AVG() fonksiyonu ile hesaplatmak, yoğun trafik alan sitelerde sunucu yükünü artırır. Bunun yerine, bir inceleme kaydedildiğinde bu puanı bir Cache anahtarında tutabilir veya ürün tablosunda rating_cache adında bir sütunda güncelleyebilirsiniz.
// Review modelinde bir Observer kullanarak puanı güncelleme
public static function booted()
{
static::saved(function ($review) {
$average = Review::where('product_id', $review->product_id)->avg('rating');
$review->product->update(['rating_cache' => $average]);
});
}
Bu yöntemle, ürün listeleme sayfalarında karmaşık hesaplamalar yerine doğrudan hazır veriyi çekerek sayfa yüklenme sürelerini milisaniyeler seviyesine indirebilirsiniz.


Yorumlar (0)
Yorum Yaz