Gereksinimler ve Ön Hazırlık
Bu sistemi kurmak için bilgisayarınızda PHP 8.2 veya üzeri bir sürümün yüklü olması önerilir. PHP 8.x sürümleri ile gelen "Constructor Property Promotion" ve "Typed Properties" gibi modern özellikler, kodumuzu daha temiz hale getirecektir.
- PHP 8.2+ (Tip güvenliği için gereklidir).
- Composer (Bağımlılık yönetimi için).
- Temel düzeyde SQL bilgisi (Veritabanı işlemleri için).
- Bir yerel sunucu ortamı (XAMPP, Laragon veya Docker).
Adım 1: İndirim Kuponu Arayüzünü (Interface) Tanımlama
Sistemin esnek olması için bir arayüz (interface) kullanacağız. Bu, tüm kupon türlerinin ortak bir sözleşmeye uymasını sağlar. Böylece ileride "Ücretsiz Kargo" veya "Sepet Bazlı İndirim" gibi yeni türleri sisteme kolayca ekleyebilirsiniz.
interface DiscountStrategy {
public function calculate(float $cartTotal): float;
}
Bu arayüz, her indirim sınıfının mutlaka bir calculate metodu içermesini zorunlu kılar. Bu sayede ana sistem, kuponun türünü bilmeden sadece hesaplama sonucunu isteyebilir.
Adım 2: Strateji Sınıflarını Oluşturma
Şimdi yüzdesel ve sabit tutarlı indirimler için somut sınıflarımızı oluşturalım. Bu sınıflar, DiscountStrategy arayüzünü uygulayacaktır.
class PercentageDiscount implements DiscountStrategy {
public function __construct(private float $percentage) {}
public function calculate(float $cartTotal): float {
return $cartTotal * ($this->percentage / 100);
}
}
class FixedAmountDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $cartTotal): float {
return $this->amount;
}
}
Burada private özellikler ve constructor kullanarak veriyi güvenli bir şekilde sınıfa dahil ettik. PercentageDiscount sınıfı toplam tutarın belirli bir yüzdesini, FixedAmountDiscount ise doğrudan belirli bir tutarı düşer.
Adım 3: İndirim Kuponu Yönetim Sınıfı
Kuponları yöneten ve indirim hesaplama sürecini yürüten ana sınıfı tasarlayalım. Bu sınıf, bir kuponun geçerlilik süresini ve limitlerini de kontrol etmelidir.
class Coupon {
public function __construct(
private string $code,
private DiscountStrategy $strategy,
private float $minSpend = 0
) {}
public function apply(float $cartTotal): float {
if ($cartTotal < $this->minSpend) {
return 0;
}
return $this->strategy->calculate($cartTotal);
}
}
Bu sınıf, bağımlılık enjeksiyonu (Dependency Injection) prensibini kullanarak DiscountStrategy nesnesini alır. Böylece Coupon sınıfı, indirimin nasıl hesaplandığıyla ilgilenmez, sadece kuralı uygular.
Adım 4: Veritabanı Entegrasyonu ve Güvenlik
Kuponları veritabanında saklarken SQL Injection saldırılarına karşı hazırlıklı olmalısınız. Daima PDO (PHP Data Objects) ile hazırlıklı ifadeler (prepared statements) kullanın.
// Güvenli veritabanı sorgusu örneği
$stmt = $pdo->prepare("SELECT * FROM coupons WHERE code = :code AND status = 1");
$stmt->execute(['code' => $_POST['coupon_code']]);
$couponData = $stmt->fetch();
Kritik Güvenlik Uyarısı: Kullanıcıdan gelen kupon kodlarını asla doğrudan SQL sorgusuna dahil etmeyin. Her zaman PDO veya benzeri bir kütüphane ile 'prepared statements' kullanarak veritabanı işlemlerini gerçekleştirin. Ayrıca, kupon kodlarını XSS (Cross-Site Scripting) saldırılarına karşı çıktı alırken
htmlspecialchars()ile filtreleyin.
Adım 5: Kupon Sistemini Test Etme
Sistemin çalıştığını doğrulamak için küçük bir senaryo oluşturalım. 1000 TL'lik bir sepetimiz ve %10 indirim yapan bir kuponumuz olsun.
$cartTotal = 1000.00;
$strategy = new PercentageDiscount(10);
$coupon = new Coupon("YAZ2026", $strategy, 500);
$discount = $coupon->apply($cartTotal);
echo "Uygulanan İndirim: " . $discount . " TL";
echo "Ödenecek Tutar: " . ($cartTotal - $discount) . " TL";
Bu kod bloğu, 500 TL alt limit kontrolünü de yaparak indirim tutarını hesaplar. Eğer sepet tutarı 500 TL'nin altındaysa, apply metodu 0 döndürecektir.
Yöntem Karşılaştırması
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| Strateji Deseni (OOP) | Modüler, test edilebilir, genişletilebilir. | Küçük projeler için fazla kod gerektirebilir. |
| Procedural (Fonksiyonel) | Hızlı yazılır, basit yapılar için uygundur. | Bakımı zordur, kod tekrarına yol açar. |
Sıkça Sorulan Sorular
Kupon sisteminde "minSpend" değeri neden önemlidir?
Minimum harcama tutarı, işletmenin kar marjını korumak için gereklidir. Çok düşük tutarlı alışverişlerde indirim uygulamak, kargo ve operasyon maliyetleri nedeniyle zarar etmenize neden olabilir.
Kupon kodlarını nasıl benzersiz yapabilirim?
Veritabanında code sütununu UNIQUE olarak tanımlayın ve PHP'nin bin2hex(random_bytes(4)) fonksiyonu ile rastgele, tahmin edilemez kodlar üretin.
Aynı anda birden fazla kupon kullanılabilir mi?
Bu sistemde Coupon nesnelerini bir dizi (array) içinde tutarak her birini sırayla sepet toplamına uygulayabilirsiniz. Ancak, indirimlerin birbirini ezmemesi için hesaplama mantığını iyi kurgulamanız gerekir.
Kuponların geçerlilik süresini nasıl eklerim?
Coupon sınıfına expires_at özelliği ekleyerek, apply metodunda new DateTime() ile karşılaştırma yapıp süresi dolan kuponları geçersiz kılabilirsiniz.
Hangi veritabanı tipi daha uygundur?
İlişkisel veritabanları (MySQL, PostgreSQL) kupon kodları, kullanım limitleri ve kullanıcı eşleşmeleri için en güvenli ve performanslı yapıyı sunar.
Kod Güvenliği Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce mutlaka giriş verilerini (input validation) doğrulayın, hata ayıklama (debug) modlarını kapatın ve veritabanı bağlantı bilgilerini güvenli bir şekilde (.env dosyası gibi) saklayın.
Kupon Sisteminde Hata Ayıklama ve Loglama Mekanizması
Dinamik bir indirim sistemi canlıya alındığında, kullanıcıların kuponları neden kullanamadığına dair geri bildirimler kritik önem taşır. "Kupon geçersiz" gibi genel bir hata mesajı yerine, sistemin neden hata verdiğini loglayan bir yapı kurmak, destek maliyetlerinizi düşürür.
Aşağıdaki örnekte, CouponManager sınıfına entegre edilebilecek basit bir hata yakalama ve loglama yapısı yer almaktadır:
class CouponLogger {
public static function log($message, $couponCode, $userId) {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] User: $userId | Coupon: $couponCode | Error: $message" . PHP_EOL;
file_put_contents('coupon_errors.log', $logEntry, FILE_APPEND);
}
}
// Kullanım örneği:
try {
$manager->applyCoupon('YAZ2024', $cartTotal, $userId);
} catch (Exception $e) {
CouponLogger::log($e->getMessage(), 'YAZ2024', $userId);
echo "İşlem başarısız: " . $e->getMessage();
}
Sistem Performansını Optimize Etme İpuçları
Kupon sistemleri, özellikle yüksek trafikli e-ticaret sitelerinde veritabanı sorgularını yoğunlaştırabilir. Her ödeme adımında veritabanına gitmek yerine, kupon verilerini önbelleğe (cache) almak performansı ciddi oranda artırır.
Redis ile Kupon Önbellekleme
Kuponun geçerlilik durumu ve indirim oranı gibi değişmeyen verileri Redis üzerinde tutarak veritabanı yükünü minimize edebilirsiniz. İşte Redis entegrasyonu için temel bir yaklaşım:
// Redis kullanarak kupon verisini çekme
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$couponCode = 'YAZ2024';
$cachedCoupon = $redis->get("coupon:$couponCode");
if ($cachedCoupon) {
$couponData = json_decode($cachedCoupon, true);
} else {
// Veritabanından çek ve Redis'e yaz
$couponData = $db->query("SELECT * FROM coupons WHERE code = '$couponCode'");
$redis->setex("coupon:$couponCode", 3600, json_encode($couponData));
}
Performans İçin İleri Tavsiyeler
- Veritabanı İndeksleme:
couponstablosundakicodesütununa mutlakaUNIQUE INDEXekleyin. Bu, sorgu hızını O(1) seviyesine yaklaştırır. - Sorgu Optimizasyonu: Kuponları çekerken
SELECT *yerine sadece ihtiyacınız olan sütunları (örn:discount_amount, type, min_spend) seçin. - Zaman Aşımı Kontrolü: Kuponun süresinin dolup dolmadığını veritabanı sorgusunda
WHERE expiry_date >= NOW()şeklinde SQL seviyesinde filtreleyerek uygulama katmanına yük bindirmeyin.
Sonuç
PHP ile nesne yönelimli bir indirim kuponu sistemi oluşturmak, kodunuzun kalitesini artırırken iş mantığınızı da koruma altına alır. Strateji tasarım desenini (Strategy Pattern) kullanarak, gelecekteki yeni indirim taleplerine kolayca uyum sağlayabilirsiniz. Bir sonraki adım olarak, bu sisteme bir "Kupon Kullanım Geçmişi" (Coupon Usage History) tablosu ekleyerek, hangi kullanıcının hangi kuponu kaç kez kullandığını takip eden bir loglama mekanizması geliştirebilirsiniz.


Yorumlar (0)
Yorum Yaz