Gereksinimler ve Ön Hazırlık
Projeye başlamadan önce geliştirme ortamınızın güncel olduğundan emin olmalısınız. PHP'nin 8.2 veya üzeri bir sürümünü kullanmanız, modern tip güvenliği (type hinting) özelliklerinden faydalanmanızı sağlar. Ayrıca, veritabanı işlemleri için PDO (PHP Data Objects) kullanacağız.
- PHP 8.2+: Modern sözdizimi ve performans iyileştirmeleri için.
- MySQL/MariaDB: Verilerin kalıcı olarak saklanması için.
- Composer: Gerekli durumlarda bağımlılık yönetimi için (bu rehberde saf PHP kullanacağız).
- Veritabanı Yapısı: Kullanıcılar ve puanlar için iki temel tabloya ihtiyacımız var.
Veritabanı Tasarımı ve İlişkisel Yapı
Dinamik bir sistem için veritabanı şemasının doğru kurgulanması hayati önem taşır. Kullanıcıların puanlarını tutacağımız bir user_ratings tablosu ve kullanıcıların kendilerini tutan bir users tablosu oluşturmalıyız. Puanlama sistemlerinde SQL Injection riskini minimize etmek için hazırlıklı sorgular (prepared statements) kullanacağız.
CREATE TABLE user_ratings (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
rater_id INT NOT NULL,
rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Bu yapı, her puanın hangi kullanıcıya ait olduğunu ve kim tarafından verildiğini takip etmemizi sağlar. TINYINT kullanımı, 1 ile 5 arasındaki puanlar için bellek verimliliği sağlar.
Puanlama Sınıfının (Class) Oluşturulması
Nesne yönelimli yapıda, puanlama işlemlerini yönetmek için bir RatingManager sınıfı oluşturacağız. Bu sınıf, veritabanı bağlantısını bir bağımlılık (dependency) olarak alacak ve puan ekleme, ortalama hesaplama gibi işlemleri gerçekleştirecektir.
class RatingManager {
private PDO $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function addRating(int $userId, int $raterId, int $rating): bool {
if ($rating < 1 || $rating > 5) return false;
$stmt = $this->db->prepare("INSERT INTO user_ratings (user_id, rater_id, rating) VALUES (?, ?, ?)");
return $stmt->execute([$userId, $raterId, $rating]);
}
}
Burada PDO tip ipucu kullanarak veritabanı bağlantısının doğru türde gelmesini garanti altına alıyoruz. addRating metodu, puanın 1 ile 5 arasında olup olmadığını kontrol ederek veri bütünlüğünü korur.
Ortalama Puan Hesaplama Mantığı
Kullanıcının profilinde gösterilecek olan ortalama puanı dinamik olarak hesaplamak, performans açısından önemlidir. SQL'in AVG fonksiyonunu kullanarak bu işlemi veritabanı seviyesinde yapmak, PHP tarafındaki yükü azaltır.
public function getAverageRating(int $userId): float {
$stmt = $this->db->prepare("SELECT AVG(rating) as avg_rating FROM user_ratings WHERE user_id = ?");
$stmt->execute([$userId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['avg_rating'] ? (float) $result['avg_rating'] : 0.0;
}
Bu metot, veritabanından dönen sonucu bir float değerine dönüştürür. Eğer kullanıcıya hiç puan verilmemişse, varsayılan olarak 0.0 değerini döndürerek uygulamanın hata vermesini engeller.
Güvenlik Uyarıları ve Hata Yönetimi
Kritik Güvenlik Uyarısı: Kullanıcıdan gelen verileri asla doğrudan SQL sorgularına dahil etmeyin. Her zaman PDO hazırlıklı sorgularını (prepared statements) kullanın. Ayrıca, bir kullanıcının kendisine puan vermesini engellemek için uygulama katmanında rater_id != user_id kontrolünü mutlaka yapın.
Aşağıdaki tabloda, puanlama sistemlerinde sıkça kullanılan yöntemlerin karşılaştırmasını bulabilirsiniz:
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| SQL AVG() | Hızlı ve verimli | Büyük veride yavaşlayabilir |
| Cache (Redis) | Çok yüksek performans | Karmaşık senkronizasyon |
| PHP Hesaplama | Esnek mantık | Yüksek bellek kullanımı |
Dinamik Puanlama Sistemini Test Etme
Sistemin çalıştığını doğrulamak için küçük bir test senaryosu oluşturalım. Bu senaryoda bir kullanıcıya farklı puanlar ekleyip ortalamayı güncel olarak alacağız.
try {
$pdo = new PDO('mysql:host=localhost;dbname=test_db', 'root', '');
$manager = new RatingManager($pdo);
// Puan ekleme
$manager->addRating(10, 1, 5);
$manager->addRating(10, 2, 4);
// Ortalama alma
echo "Kullanıcı 10 için ortalama: " . $manager->getAverageRating(10);
} catch (PDOException $e) {
echo "Bağlantı hatası: " . $e->getMessage();
}
Bu kod bloğu, veritabanı bağlantısını kurar, RatingManager sınıfını başlatır ve örnek verilerle sistemi test eder. Hata yakalama (try-catch) bloğu, veritabanı hatalarını kullanıcıya göstermeden yönetmenizi sağlar.
Sıkça Sorulan Sorular
Bir kullanıcı aynı kişiye birden fazla puan verebilir mi?
Genellikle hayır. Veritabanı tasarımında user_id ve rater_id sütunlarına "unique" kısıtlaması ekleyerek veya addRating metodunda mevcut bir kayıt olup olmadığını kontrol ederek bunu engelleyebilirsiniz.
Puanlama sistemini nasıl ölçeklendirebilirim?
Veri miktarı arttığında, ortalama puanı her seferinde hesaplamak yerine, kullanıcı tablosuna average_rating ve total_ratings sütunları ekleyerek puan her güncellendiğinde bu değerleri güncelleyen bir tetikleyici (trigger) veya arka plan işi (background job) kullanabilirsiniz.
Puanlama sisteminde XSS saldırılarına karşı ne yapmalıyım?
Puanlar sayısal değerlerdir, ancak puanlama ile birlikte yorum da alıyorsanız, kullanıcı girdilerini htmlspecialchars() fonksiyonu ile filtreleyerek XSS saldırılarını önlemelisiniz.
Neden PDO kullanmalıyım?
PDO, veritabanından bağımsız bir arayüz sunar ve SQL Injection saldırılarına karşı en güvenli yöntem olan hazırlıklı sorguları (prepared statements) destekler.
Puanlama aralığını değiştirmek istersem ne yapmalıyım?
Veritabanındaki CHECK kısıtlamasını güncelleyip, RatingManager sınıfındaki addRating metodundaki koşulu değiştirmeniz yeterlidir.
Puanlama Sisteminde Performans Optimizasyonu
Sisteminiz büyüdükçe ve puanlama verisi arttıkça, her sayfa yüklenişinde AVG() fonksiyonu ile tüm tabloyu taramak performans kayıplarına yol açabilir. Özellikle milyonlarca satırlık bir veritabanında, anlık hesaplama yapmak yerine "Önbellekleme (Caching)" veya "Denormalizasyon" yöntemlerini kullanmak en mantıklı yaklaşımdır.
Denormalizasyon Stratejisi: Kullanıcılar tablonuza average_rating ve rating_count adında iki sütun ekleyerek, her yeni puan eklendiğinde bu değerleri arka planda güncelleyebilirsiniz. Böylece veriyi okurken karmaşık sorgular yerine doğrudan bu sütunları çekersiniz.
// Puan eklendiğinde tetiklenecek güncelleyici metod örneği
public function updateRatingCache($targetUserId) {
$sql = "UPDATE users SET
average_rating = (SELECT AVG(score) FROM ratings WHERE target_id = :id),
rating_count = (SELECT COUNT(*) FROM ratings WHERE target_id = :id)
WHERE id = :id";
$stmt = $this->db->prepare($sql);
$stmt->execute(['id' => $targetUserId]);
}
Sistem Hatalarını Ayıklama ve Loglama
Üretim ortamında kullanıcıların puanlama yaparken karşılaştığı hataları (veritabanı bağlantı kopmaları, kilitlenmeler vb.) izlemek kritiktir. Standart try-catch bloklarının ötesine geçerek, hataları bir dosyaya veya merkezi bir loglama sistemine kaydetmelisiniz.
Aşağıdaki örnek, hata oluştuğunda sistemin sessiz kalmasını engeller ve geliştiriciye hata detayını sunar:
try {
$ratingManager->addRating($userId, $targetId, $score);
} catch (Exception $e) {
// Hatayı log dosyasına yaz
error_log("[" . date('Y-m-d H:i:s') . "] Puanlama Hatası: " . $e->getMessage(), 3, 'logs/error.log');
// Kullanıcıya dostane bir mesaj göster
echo json_encode(['status' => 'error', 'message' => 'Puanlama sırasında bir sorun oluştu.']);
}
İleri Seviye İpuçları: Puan Ağırlıklandırma
Sisteminizi daha adil hale getirmek için "Ağırlıklı Ortalama" (Weighted Average) yöntemini kullanabilirsiniz. Örneğin, yeni verilen puanlara daha fazla ağırlık vererek, kullanıcının güncel performansının daha hızlı yansımasını sağlayabilirsiniz.
- Zaman Damgası Faktörü: Eski puanların etkisini zamanla azaltan bir katsayı uygulayın.
- Kullanıcı Güvenilirliği: Puan veren kullanıcının kendi "itibar puanı" yüksekse, verdiği puanın ortalamaya etkisi daha fazla olsun.
- Bot Koruması: Çok kısa sürede çok fazla puan girişi yapan IP adreslerini geçici olarak engelleyen bir "Rate Limiting" mekanizması kurun.
Bu geliştirmeler, basit bir puanlama sistemini kurumsal ölçekte bir değerlendirme platformuna dönüştürmenize olanak tanır. Kodunuzu modüler tutarak, ileride eklemek isteyeceğiniz "yorum ekleme" veya "puanı geri çekme" gibi özellikleri kolayca entegre edebilirsiniz.
Sonuç
PHP ile nesne yönelimli yapıda dinamik bir kullanıcı puanlama sistemi kurmak, hem veritabanı optimizasyonu hem de temiz kod prensiplerini bir araya getiren harika bir pratik çalışmadır. Bu rehberde öğrendiğiniz PDO kullanımı, sınıf mimarisi ve güvenlik önlemleri, profesyonel projeleriniz için temel teşkil edecektir.
Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce mutlaka detaylı güvenlik testlerinden geçirin ve hata ayıklama (debug) modunu kapatın.
Bir sonraki adım olarak, bu sisteme "puanlama geçmişi" veya "kullanıcı bazlı puanlama istatistikleri" gibi özellikler ekleyerek sisteminizi geliştirebilirsiniz. Başarılar dilerim.

Yorumlar (0)
Yorum Yaz