🛒 Cart Modülü & Sepete Ekle Butonu - Kapsamlı Analiz

📅 Tarih: 2025-12-02 | 🎯 Tenant: ixtif.com (Tenant 2 - Endüstriyel Ekipman) | 👤 Talep: Cart modülü ve homepage add to basket butonu analizi

🏗️ Sistem Mimarisi

Multi-Tenant E-Ticaret Sistemi

Sistem, Multi-Tenant (Çok kiracılı) mimari üzerine kurulu. Her tenant'ın kendi database'i var ve sepet verileri tenant database'lerinde saklanıyor.

Modüler Yapı: Cart modülü ve Shop modülü birbirinden bağımsız ama Bridge Service (Köprü Servisi) ile entegre çalışıyor.

📦 Cart Modülü
Sorumluluk: Sepet yönetimi, item ekleme/çıkarma, fiyat hesaplama, vergi, kupon, sipariş dönüşümü
  • CartService: İş mantığı merkezi
  • Cart Model: Sepet verisi (session/customer tracking)
  • CartItem Model: Sepet içerikleri (polymorphic)
  • CartWidget: Header'da mini sepet (Livewire)
  • CartApiController: API endpoint'leri
🛍️ Shop Modülü
Sorumluluk: Ürün yönetimi, stok kontrolü, fiyatlandırma, cart entegrasyonu
  • ShopProduct Model: Ürün verisi
  • ShopCartBridge: Shop → Cart entegrasyonu
  • AddToCartButton: Sepete ekle component (Livewire)
  • Product Card: Ürün kartı (Alpine.js + API)

🔄 Veri Akışı - Sepete Ekleme İşlemi

Homepage Add to Cart Flow (Alpine.js + API)

1
Kullanıcı Tıklar
Product card üzerindeki "Sepete Ekle" butonuna tıklar. Alpine.js addToCart() metodu tetiklenir.
2
localStorage Cart ID Kontrolü
Alpine.js, localStorage'dan cart_id okur. Varsa API'ye gönderilir (persistent cart).
3
API Request
POST /api/cart/add endpoint'ine istek gönderilir. Payload: { product_id, quantity, cart_id }
4
CartApiController
Gelen isteği alır, ürünü database'den çeker, ShopCartBridge ile stok/fiyat kontrolü yapar.
5
CartService - Sepet Bulma
1. Öncelik: localStorage'dan gelen cart_id ile sepet bulunur.
2. Fallback: Session ID ile sepet bulunur.
3. Oluşturma: Yoksa yeni sepet oluşturulur.
6
CartService - Item Ekleme
Aynı ürün zaten sepette mi?
→ Varsa: Quantity artırılır, fiyatlar yeniden hesaplanır.
→ Yoksa: Yeni CartItem oluşturulur. Fiyat, vergi, currency conversion hesaplanır.
7
Cart Recalculation
Sepet toplamları yeniden hesaplanır: subtotal, tax_amount, total, items_count
8
API Response
Başarılı response döner: { success: true, data: { cart_id, item_count, total } }
9
Frontend Update
1. localStorage Güncelleme: cart_id kaydedilir.
2. Event Dispatch: window.dispatchEvent('cart-updated') ve Livewire.dispatch('cartUpdated')
3. CartWidget Refresh: Header'daki mini sepet güncellenir (badge + item count).

Shop Pages Add to Cart Flow (Livewire)

1
Kullanıcı Tıklar
Ürün detay/kategori sayfasında "Sepete Ekle" butonuna tıklar. Livewire AddToCartButton component tetiklenir.
2
Livewire Component (AddToCartButton)
Server-side işlem başlar. Session/Customer ID otomatik tanınır (Livewire native).
3
CartService - Direct Call
API kullanmadan doğrudan CartService::addItem() çağrılır. Aynı mantık: sepet bul/oluştur, item ekle, hesapla.
4
Event Dispatch (Livewire)
$this->dispatch('cartUpdated') ve $this->js('window.dispatchEvent...') ile CartWidget bilgilendirilir.
5
CartWidget Refresh
Livewire CartWidget component #[On('cartUpdated')] ile güncellenir.

🔧 Cart Modülü - Teknik Detaylar

CartService - İş Mantığı Merkezi

Sorumluluk: Tüm sepet işlemlerinin merkezi koordinasyonu.

  • getCart(): Session veya Customer ID ile mevcut sepeti bulur.
  • findOrCreateCart(): Sepet bulur veya oluşturur (guest/registered user için).
  • addItem(): Ürünü sepete ekler. Aynı ürün varsa quantity artırır, yoksa yeni CartItem oluşturur.
  • updateItemQuantity(): Sepetteki ürün miktarını günceller.
  • removeItem(): Sepetten ürün kaldırır.
  • clearCart(): Sepeti tamamen boşaltır.
  • applyCoupon(): İndirim kuponu uygular.
  • convertToOrder(): Sepeti siparişe dönüştürür.
  • mergeGuestCart(): Misafir sepetini kayıtlı kullanıcı sepetine birleştirir (login sonrası).

💡 Önemli Özellik: ensureTenantContext() metodu sayesinde central database'de yanlışlıkla cart sorgusu yapılması engellenir. Tenant context yoksa işlem yapılmaz.

Cart Model - Sepet Verisi

Database Tablosu: carts

  • customer_id: Kayıtlı kullanıcı (nullable - guest için null)
  • session_id: Session tracking (guest için zorunlu)
  • status: active, converted, abandoned, merged
  • items_count: Toplam ürün adedi
  • subtotal: Ara toplam (vergi hariç)
  • tax_amount: Toplam vergi tutarı
  • total: Genel toplam (vergi dahil)
  • coupon_code + coupon_discount: İndirim kuponu
  • billing_address + shipping_address: Adres bilgileri (JSON)
  • abandoned_at: Terk edilme zamanı
  • recovery_token: Sepet kurtarma linki için

💡 Tenant Protection: Model'de booted() metodu ile global scope ekleniyor. Tenant context yoksa hiçbir sorgu sonuç döndürmez.

CartItem Model - Polymorphic Items

Database Tablosu: cart_items

  • cartable_type + cartable_id: Polymorphic Relationship (Esnek ilişki - ShopProduct, Subscription, Service vb. olabilir)
  • product_id: Backward compatibility için (eski sistemlerle uyumluluk)
  • quantity: Ürün adedi
  • unit_price: Birim fiyat
  • discount_amount: İndirim miktarı
  • final_price: İndirimli fiyat (unit_price - discount)
  • tax_rate + tax_amount: Vergi oranı ve tutarı
  • subtotal: Ara toplam (final_price × quantity)
  • total: Toplam (subtotal + tax_amount)
  • item_title, item_image, item_sku: Display bilgileri (snapshot - ürün silinse bile sepette görünür)
  • original_currency, original_price, conversion_rate: Para birimi dönüşümü metadata

💡 Polymorphic Design: Bu sayede sadece Shop ürünleri değil, Subscription planları, Service hizmetleri de sepete eklenebilir. Esnek yapı!

CartWidget - Header Mini Sepet (Livewire)

Sorumluluk: Header'da sepet badge'i ve dropdown menu.

  • mount(): Component başlatıldığında sepet yüklenmez (localStorage ile senkron kalmak için).
  • refreshCart(): @On('cartUpdated') event'i ile tetiklenir. Session ile cart bulup yükler.
  • refreshCartById(): localStorage'dan gelen cart_id ile direkt yükleme (performanslı).
  • removeItem(): Mini sepetten ürün silme.
  • increaseQuantity() / decreaseQuantity(): Mini sepetten miktar değiştirme.

⚠️ İyileştirme Fırsatı: localStorage sync gecikmesi var. @script directive ile sayfa yüklendiğinde cart_id localStorage'dan okunup refreshCartById() tetiklenmeli.

🔗 Shop Modülü Entegrasyonu

ShopCartBridge - Köprü Servisi

Amaç: Shop modülü ile Cart modülü arasındaki veri dönüşümü ve iş kurallarını yönetir.

prepareProductForCart() Metodu:

  • Stok Kontrolü: validateStock() ile stok yeterliliği kontrol edilir. stock_tracking aktifse ve stok yetersizse, allow_backorder kontrolü yapılır.
  • Display Bilgileri: getProductDisplayInfo() ile ürün başlığı, resmi, SKU kodu alınır. Snapshot olarak cart_items tablosuna kaydedilir.
  • Fiyat Bilgileri: getProductPriceInfo() ile final_price, currency bilgisi alınır.
  • Vergi Bilgileri: getProductTaxInfo() ile tax_rate alınır (varsayılan %20 KDV).

canAddToCart() Metodu:

  • Ürün aktif mi? (is_active)
  • Stok yeterli mi? (stok takibi varsa)
  • Backorder izni var mı? (stok yoksa)

getCartItemErrors() Metodu:

Kullanıcıya gösterilecek hata mesajlarını döner:

  • "Ürün şu anda satışta değil."
  • "Stok yetersiz. Mevcut stok: X"
  • "Ürün şu anda stokta yok. Sipariş verebilirsiniz, tedarik süresi: X gün."

AddToCartButton - Livewire Component (Shop Sayfalarında)

Kullanım Yeri: Ürün detay sayfası, kategori sayfası.

Özellikler:

  • Quantity Selector: showQuantity prop'u ile adet seçimi gösterilebilir.
  • Loading State: isAdding ile buton disable edilir, spinner gösterilir.
  • Event Dispatch: Sepete eklenince hem Livewire event (cartUpdated) hem browser event (cart-updated) gönderilir.

🎯 Akış:

  1. Kullanıcı buton tıklar → addToCart() metodu çağrılır.
  2. Stok kontrolü → ShopCartBridge::canAddToCart()
  3. Session/Customer ID alınır.
  4. CartService ile sepet bulunur/oluşturulur.
  5. Bridge ile ürün bilgileri hazırlanır (prepareProductForCart()).
  6. CartService ile item eklenir.
  7. Event dispatch → CartWidget refresh.

🏠 Homepage Product Card - Alpine.js + API

Product Card Component

Dosya: resources/views/components/ixtif/product-card.blade.php

Kullanım Yeri: Homepage ürün listesi.

Özellikler:

  • Alpine.js Component: x-data="productCard()" ile client-side reactive state.
  • API Call: addToCart() metodu fetch('/api/cart/add') ile API'ye istek gönderir.
  • localStorage Sync: cart_id localStorage'a kaydedilir ve API'ye gönderilir.
  • Loading/Success States: loading ve success reactive değişkenleri ile buton durumu kontrol edilir.
  • Event Dispatch: API başarılı response dönünce window.dispatchEvent('cart-updated') ve Livewire.dispatch('cartUpdated') gönderilir.

🎯 Akış:

  1. Kullanıcı buton tıklar → Alpine.js addToCart() tetiklenir.
  2. localStorage'dan cart_id okunur.
  3. POST /api/cart/add isteği gönderilir (product_id, quantity, cart_id).
  4. API response → başarılıysa cart_id localStorage'a kaydedilir.
  5. Event dispatch → CartWidget refresh.
  6. Success state → 2 saniye sonra reset.

CartApiController - API Endpoint

Endpoint: POST /api/cart/add

Request Validation:

  • product_id - zorunlu, integer, shop_products tablosunda var mı?
  • quantity - opsiyonel, integer, minimum 1 (varsayılan: 1)
  • variant_id - opsiyonel, integer (varyant desteği)
  • cart_id - opsiyonel, integer (localStorage'dan gelen persistent cart)

İşlem Adımları:

  1. Ürünü database'den çek (ShopProduct::findOrFail()).
  2. Stok kontrolü (ShopCartBridge::canAddToCart()).
  3. Session ID ve Customer ID al.
  4. localStorage'dan gelen cart_id ile sepet bul. Yoksa session ile bul/oluştur.
  5. Bridge ile ürün bilgilerini hazırla (prepareProductForCart()).
  6. CartService ile item ekle.
  7. Cart refresh → item_count hesapla.
  8. JSON response dön: { success, message, data: { cart_id, item_count, total } }

💡 Önemli: API, cart_id parametresini destekliyor. Bu sayede localStorage ile persistent cart çalışıyor. Kullanıcı sayfayı yenileyip sepete ürün eklese bile aynı sepet kullanılıyor.

Güçlü Yönler

Polymorphic Design - Esnek Item Sistemi: CartItem modeli polymorphic relationship kullanıyor. Sadece Shop ürünleri değil, Subscription, Service, Course gibi her türlü item sepete eklenebilir. Gelecekte yeni item türleri eklemek çok kolay.
Currency Conversion - Döviz Dönüşümü: Ürünler USD/EUR gibi dövizlerle fiyatlandırılabilir. CartService otomatik olarak TRY'ye çevirir. original_currency, original_price, conversion_rate metadata olarak saklanır.
Stock Validation - Stok Kontrolü: ShopCartBridge servisi ile stok kontrolü yapılıyor. stock_tracking, allow_backorder, lead_time_days gibi özellikler destekleniyor.
Tax Calculation - Vergi Hesaplama: Her item için ayrı vergi oranı (tax_rate) saklanıyor. Sepet toplamında vergi ayrı ayrı hesaplanıp gösteriliyor.
Guest + Registered User Support - Misafir ve Üye Desteği: Hem misafir kullanıcılar (session_id ile) hem kayıtlı kullanıcılar (customer_id ile) sepet oluşturabiliyor. Login sonrası mergeGuestCart() ile sepetler birleştiriliyor.
API-First Approach - API Öncelikli Yaklaşım: Homepage product card API kullanıyor. Bu sayede gelecekte mobile app, SPA, headless commerce gibi farklı frontend'ler kolayca entegre edilebilir.
Event-Driven Updates - Olay Bazlı Güncellemeler: Sepete ürün eklenince CartWidget otomatik güncelleniyor. Event sistemi sayesinde farklı componentler birbirine bağlı çalışıyor.
localStorage Persistence - Kalıcı Sepet: cart_id localStorage'da saklanıyor. Kullanıcı sayfayı yenilese bile sepet korunuyor.
Tenant Context Protection - Tenant Koruma: Cart ve CartItem modellerinde global scope ile tenant context kontrolü yapılıyor. Central database'de yanlışlıkla cart sorgusu yapılması engelleniyor.
Display Data Snapshot - Veri Anlık Görüntüsü: item_title, item_image, item_sku gibi display bilgileri cart_items tablosuna kaydediliyor. Ürün silinse veya güncellense bile sepette doğru bilgiler gösteriliyor.

İyileştirme Fırsatları

Homepage vs Shop Pages - Tutarlılık Eksikliği: Homepage product card Alpine.js + API kullanırken, shop sayfaları Livewire kullanıyor. İki farklı yaklaşım kafa karıştırıcı. Öneri: Tüm sayfalar API kullansın veya tüm sayfalar Livewire kullansın.
CartWidget localStorage Sync - Senkronizasyon Gecikmesi: CartWidget component mount() metodunda sepet yüklenmiyor. localStorage'da cart_id varsa sayfa yüklendiğinde CartWidget otomatik refresh edilmeli. Öneri: @script directive ile DOMContentLoaded event'inde refreshCartById() çağır.
Event Duplication - Olay Duplikasyonu: Hem Livewire.dispatch('cartUpdated') hem window.dispatchEvent('cart-updated') kullanılıyor. İki farklı event sistemi gereksiz. Öneri: Tek bir event sistemi seç (Livewire events daha güçlü, Alpine.js ile de uyumlu).
Error Handling - Hata Yönetimi: API hata mesajları kullanıcıya alert() ile gösteriliyor. Modern UX için toast notification veya inline error message kullanılmalı. Öneri: Alpine.js notification component veya Livewire flash messages.
Optimistic Updates - İyimser Güncellemeler: Sepete ürün eklenince buton "loading" state'e geçiyor ama CartWidget hemen güncellenmiyor. API response beklenene kadar kullanıcı badge güncellemesini görmüyor. Öneri: Optimistic update - item_count anında artırılsın, API fail olursa geri alınsın.
Cart Abandonment - Terk Edilen Sepet: abandoned_at, recovery_token, recovery_email_sent_at fieldları var ama recovery sistemi pasif. Öneri: Cron job ile terk edilen sepetler tespit edilip kullanıcıya e-posta gönderilebilir.
Real-time Stock Check - Gerçek Zamanlı Stok Kontrolü: Sepete ekleme sırasında stok kontrolü yapılıyor ama sepet sayfasında güncel stok durumu kontrol edilmiyor. Kullanıcı 1 saat önce eklediği ürünü satın almaya çalışırken stok bitmiş olabilir. Öneri: Checkout başlamadan önce checkStock() metodu çağrılmalı.
Coupon Validation - Kupon Doğrulama: applyCoupon() metodu var ama kupon validasyonu yok. Kupon kodu kontrol edilmiyor, indirim miktarı direk parametre olarak geliyor. Öneri: Coupon modülü eklenmeli, kupon kodları database'de tutulmalı, geçerlilik, kullanım limiti, minimum tutar gibi kurallar uygulanmalı.
Performance - N+1 Query Problemi: Product card'da getFirstMediaWithFallback() her ürün için ayrı media sorgusu yapıyor olabilir. Öneri: Homepage controller'da ürünler çekilirken with('media') ile eager loading yapılmalı.
Analytics - Sepet Analitiği: Sepete ekleme, sepetten çıkarma, terk edilme gibi olaylar track edilmiyor. Öneri: Google Analytics veya internal analytics sistemi ile sepet olayları loglanmalı. Hangi ürünler çok sepete ekleniyor ama satın alınmıyor? (conversion funnel analysis)

🚀 Geliştirme Önerileri

Kısa Vadeli İyileştirmeler (1-2 Hafta)

  1. CartWidget localStorage sync düzelt: Sayfa yüklendiğinde localStorage cart_id ile otomatik refresh.
  2. Toast notification ekle: Alpine.js notification component ile kullanıcıya başarı/hata mesajları göster.
  3. Optimistic update: Sepete ekleme butonunda anında success state, API fail olursa geri al.
  4. Event sistemi standartlaştır: Sadece Livewire events kullan veya sadece browser events kullan.
  5. Homepage product card eager loading: N+1 query problemini çöz.

Orta Vadeli İyileştirmeler (1-2 Ay)

  1. Coupon modülü: Kupon kodu validasyonu, kullanım limiti, minimum tutar, geçerlilik tarihi.
  2. Real-time stock check: Checkout öncesi sepetteki tüm ürünlerin stok durumunu kontrol et.
  3. Cart abandonment recovery: Cron job ile terk edilen sepetler tespit et, recovery e-postası gönder.
  4. Analytics integration: Google Analytics veya internal analytics ile sepet olaylarını track et.
  5. Homepage/Shop sayfaları tutarlılık: Tüm sayfalarda aynı add to cart yaklaşımını kullan (API veya Livewire - birini seç).

Uzun Vadeli İyileştirmeler (3-6 Ay)

  1. Wishlist entegrasyonu: Sepet ile wishlist arasında ürün taşıma (moved_from_wishlist field var ama kullanılmıyor).
  2. Cart sharing: Kullanıcı sepetini başkasıyla paylaşabilsin (recovery_token ile public cart URL).
  3. Saved carts: Kullanıcı birden fazla sepet oluşturabilsin ("Ofis Alışverişi", "Ev Alışverişi" gibi).
  4. Bulk operations: Sepetteki tüm ürünleri hızlıca temizle, wishlist'e taşı, başka sepete kopyala.
  5. Subscription support: Subscription planları için recurring cart (aylık/yıllık otomatik yenileme).
  6. Mobile app API: Tüm cart işlemleri için RESTful API dokümantasyonu ve versiyonlama.

📊 Genel Değerlendirme

Sistem Sağlamlığı: 8/10

Güçlü Yönler: Polymorphic design, currency conversion, stock validation, tax calculation, guest/registered user support, event-driven updates.

İyileştirme Alanları: CartWidget localStorage sync, event duplication, error handling, optimistic updates, coupon validation, analytics.

Kod Kalitesi: 7.5/10

İyi Yanlar: Modüler yapı, service pattern, bridge pattern, tenant protection, display data snapshot.

İyileştirilmesi Gerekenler: Homepage vs Shop tutarlılığı, N+1 query problemi, error handling, analytics eksikliği.

Kullanıcı Deneyimi: 7/10

İyi Yanlar: localStorage persistence, mini cart widget, loading states, success feedback.

İyileştirilmesi Gerekenler: Toast notifications, optimistic updates, real-time stock check, cart abandonment recovery.

Ölçeklenebilirlik: 9/10

Neden Yüksek? API-first approach, polymorphic design, multi-tenant architecture, event-driven updates. Gelecekte mobile app, SPA, headless commerce kolayca entegre edilebilir.

Öncelikli Aksiyonlar
  1. CartWidget localStorage sync düzelt (en kritik - sayfa yenilemede sepet badge'i kaybolmasın).
  2. Toast notification ekle (kullanıcı deneyimi - alert() çirkin).
  3. Homepage/Shop tutarlılığı sağla (kod kalitesi - iki yaklaşım kafa karıştırıcı).
  4. Real-time stock check ekle (iş mantığı - checkout'ta stok bitmiş hatası almasın).
  5. Analytics integration (veri odaklı karar - hangi ürünler sepete ekleniyor ama satın alınmıyor?).