Php İle Nesne Yönelimli Yapıda Dinamik Site İçi Arama Geçmişi Nasıl Yapılır?

Php İle Nesne Yönelimli Yapıda Dinamik Site İçi Arama Geçmişi Nasıl Yapılır?
Php İle Nesne Yönelimli Yapıda Dinamik Site İçi Arama Geçmişi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Bu projeyi başarıyla hayata geçirmek için sunucunuzda veya yerel geliştirme ortamınızda aşağıdaki bileşenlerin yüklü olması gerekmektedir:

  • PHP 8.2 veya üzeri (Modern tip tanımlamaları ve performans iyileştirmeleri için).
  • MySQL veya MariaDB veritabanı sunucusu.
  • PDO (PHP Data Objects) eklentisinin aktif olması (Veritabanı güvenliği için zorunludur).
  • Temel düzeyde SQL bilgisi ve nesne yönelimli programlama prensiplerine (Class, Property, Method) aşinalık.

Veritabanı Şemasının Tasarlanması

Arama geçmişini tutmak için verimli bir veritabanı yapısı kurmalıyız. Her arama kaydı, kullanıcı kimliği ile ilişkilendirilmelidir. Aşağıdaki SQL sorgusu, arama geçmişi için gerekli tabloyu oluşturur.

CREATE TABLE search_history (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    search_query VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX(user_id)
) ENGINE=InnoDB;

Bu tablo yapısında user_id sütununa indeks eklemek, arama geçmişini çekerken sorgu performansını ciddi oranda artırır. created_at sütunu sayesinde en son yapılan aramaları kolayca sıralayabiliriz.

Veritabanı Bağlantı Sınıfı

Nesne yönelimli yapıda veritabanı işlemlerini yönetmek için bir Database sınıfı oluşturmalıyız. Bu sınıf, PDO'yu kullanarak güvenli bağlantı sağlar.

class Database {
    private static ?PDO $instance = null;

    public static function getConnection(): PDO {
        if (self::$instance === null) {
            $dsn = "mysql:host=localhost;dbname=uygulama_db;charset=utf8mb4";
            self::$instance = new PDO($dsn, 'db_user', 'db_password', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        }
        return self::$instance;
    }
}

Bu sınıf, Singleton tasarım desenine benzer bir mantıkla çalışarak uygulama boyunca tek bir veritabanı bağlantısı kullanılmasını sağlar. Bu yöntem, kaynak tüketimini optimize eder.

Arama Geçmişi Yönetimi İçin SearchManager Sınıfı

Arama geçmişini eklemek, silmek ve listelemek için SearchManager sınıfını oluşturacağız. Bu sınıf, veritabanı işlemlerini soyutlayarak iş mantığını (business logic) arayüzden ayırır.

class SearchManager {
    private PDO $db;

    public function __construct(PDO $db) {
        $this->db = $db;
    }

    public function addSearch(int $userId, string $query): void {
        $stmt = $this->db->prepare("INSERT INTO search_history (user_id, search_query) VALUES (?, ?)");
        $stmt->execute([$userId, htmlspecialchars($query, ENT_QUOTES, 'UTF-8')]);
    }

    public function getHistory(int $userId, int $limit = 5): array {
        $stmt = $this->db->prepare("SELECT search_query FROM search_history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?");
        $stmt->execute([$userId, $limit]);
        return $stmt->fetchAll();
    }
}

Burada htmlspecialchars fonksiyonunu kullanarak XSS (Cross-Site Scripting) saldırılarına karşı önlem alıyoruz. Ayrıca PDO'nun hazırlanan ifadeleri (prepared statements) sayesinde SQL Injection riskini tamamen ortadan kaldırıyoruz.

Dinamik Arama Geçmişi Uygulama Yöntemleri

Arama geçmişini sunarken farklı yöntemler tercih edilebilir. Aşağıdaki tablo, bu yöntemlerin avantaj ve dezavantajlarını özetlemektedir.

Yöntem Avantajı Dezavantajı
Veritabanı (MySQL) Kalıcı ve senkronize veri. Sunucu yükünü bir miktar artırır.
LocalStorage (JS) Sunucu yükü yok, çok hızlı. Cihaz değişince geçmiş kaybolur.
Session (PHP) Basit uygulama. Tarayıcı kapanınca veriler silinir.

Güvenlik Uyarısı: Kullanıcıdan alınan verileri veritabanına kaydetmeden önce mutlaka temizleyin ve doğruluğunu kontrol edin. Asla doğrudan $_GET veya $_POST verilerini sorgulara dahil etmeyin.

Arama Geçmişini Arayüzde Gösterme

Kullanıcının arama geçmişini sayfada göstermek için SearchManager sınıfını kullanacağız. Bu örnekte, basit bir döngü ile geçmiş verilerini ekrana basıyoruz.

$db = Database::getConnection();
$searchManager = new SearchManager($db);
$userId = $_SESSION['user_id'] ?? 0;

if ($userId > 0) {
    $history = $searchManager->getHistory($userId);
    echo "

Son Aramalarınız

    "; foreach ($history as $item) { echo "
  • " . htmlspecialchars($item['search_query']) . "
  • "; } echo "
"; }

Bu kod bloğu, kullanıcının oturum açıp açmadığını kontrol eder ve veritabanından çektiği verileri güvenli bir şekilde HTML listesi olarak sunar.

Sıkça Sorulan Sorular

Arama geçmişi veritabanı yerine Redis üzerinde tutulabilir mi?

Evet, özellikle yüksek trafikli sitelerde arama geçmişi gibi sık erişilen veriler için Redis gibi anahtar-değer (key-value) depolama sistemleri çok daha hızlı sonuç verir.

Aynı arama teriminin birden fazla kez kaydedilmesini nasıl engellerim?

addSearch metodunu güncellemeden önce, veritabanında aynı kullanıcı için aynı terimin olup olmadığını kontrol eden bir SELECT sorgusu çalıştırabilir veya veritabanı tablonuza UNIQUE bir kısıtlama ekleyebilirsiniz.

Arama geçmişi verileri ne kadar süre saklanmalıdır?

Kullanıcı gizliliği açısından, arama geçmişini 30 veya 60 gün gibi makul sürelerle sınırlamak ve eski kayıtları otomatik silen bir cron job (zamanlanmış görev) kurmak en iyi pratiktir.

Bu yapı mobil uygulamalarla uyumlu mu?

Evet, oluşturduğumuz sınıf yapısı bir API aracılığıyla JSON formatında veri döndürecek şekilde kolayca genişletilebilir.

Performans için arama geçmişini nasıl önbelleğe alabilirim?

Sorgu sonuçlarını APCu veya Memcached kullanarak belirli bir süre bellekte tutabilir, veritabanı yükünü minimize edebilirsiniz.

Arama Geçmişi Sisteminde Hata Ayıklama ve Loglama Stratejileri

Geliştirdiğiniz arama geçmişi modülünün üretim ortamında sorunsuz çalışmasını sağlamak için sağlam bir hata ayıklama mekanizması kurmalısınız. Özellikle veritabanı bağlantı hataları veya geçersiz sorgu parametreleri, kullanıcı deneyimini doğrudan etkileyebilir. PHP'nin try-catch bloklarını SearchManager sınıfınızın içine entegre ederek, hataları sessizce yutmak yerine log dosyalarına kaydetmek en iyi yaklaşımdır.

Aşağıdaki örnek, bir arama kaydı sırasında oluşabilecek hataları nasıl yakalayacağınızı ve loglayacağınızı göstermektedir:

public function saveSearch($userId, $query) {
    try {
        if (empty(trim($query))) {
            throw new Exception("Boş arama terimi kaydedilemez.");
        }
        
        $stmt = $this->db->prepare("INSERT INTO search_history (user_id, search_term) VALUES (?, ?)");
        return $stmt->execute([$userId, $query]);
        
    } catch (PDOException $e) {
        error_log("Veritabanı Hatası: " . $e->getMessage());
        return false;
    } catch (Exception $e) {
        error_log("Genel Hata: " . $e->getMessage());
        return false;
    }
}

Bu yapı sayesinde, sisteminizde bir sorun oluştuğunda error_log üzerinden hatanın kaynağını tespit edebilir ve kullanıcıya "İşlem başarısız" gibi genel bir hata mesajı göstererek sistem detaylarınızı gizli tutabilirsiniz.

İleri Seviye İpuçları: Arama Geçmişinde Veri Temizliği ve Optimizasyon

Arama geçmişi tabloları, yoğun trafik alan sitelerde çok hızlı bir şekilde büyüyebilir. Bu durum, veritabanı sorgularının yavaşlamasına neden olur. Veri tabanınızın performansını korumak için belirli periyotlarla "temizlik" (cleanup) işlemleri yapmalısınız. Örneğin, 30 günden eski kayıtları otomatik olarak silen bir cron job veya veritabanı tetikleyicisi (trigger) kullanmak, tablonun boyutunu kontrol altında tutar.

Ayrıca, kullanıcı bazlı arama geçmişi limitleri koyarak tek bir kullanıcının veritabanını şişirmesini engelleyebilirsiniz:

public function limitUserHistory($userId, $limit = 10) {
    // Kullanıcının en eski kayıtlarını silerek sadece son 10 aramayı tutar
    $sql = "DELETE FROM search_history 
            WHERE id NOT IN (
                SELECT id FROM (
                    SELECT id FROM search_history 
                    WHERE user_id = ? 
                    ORDER BY created_at DESC 
                    LIMIT ?
                ) AS subquery
            ) AND user_id = ?";
    
    $stmt = $this->db->prepare($sql);
    return $stmt->execute([$userId, $limit, $userId]);
}

Bu yöntemle, her yeni arama kaydedildiğinde veya belirli aralıklarla bu metodu çağırarak veritabanınızın her zaman optimize kalmasını sağlayabilirsiniz. Kullanıcı deneyimi açısından ise, arama geçmişini sadece veritabanından çekmek yerine, ilk yüklemede localStorage üzerinde tutulan verilerle birleştirerek (hybrid yaklaşım) sorgu sayısını minimize edebilirsiniz.

Sonuç

Php ile nesne yönelimli yapıda dinamik bir arama geçmişi oluşturmak, hem kod kalitenizi artırır hem de kullanıcılarınıza kişiselleştirilmiş bir deneyim sunmanızı sağlar. Bu rehberde öğrendiğiniz PDO kullanımı, sınıf yapıları ve güvenlik önlemleri, profesyonel PHP geliştirme sürecinin temel taşlarıdır. Bir sonraki adım olarak, bu sisteme AJAX desteği ekleyerek sayfa yenilenmeden arama önerileri sunan bir "Autocomplete" (otomatik tamamlama) özelliği entegre etmeyi deneyebilirsiniz.

Yasal Uyarı: Kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce hata yönetimi (try-catch blokları), loglama ve kapsamlı bir güvenlik testinden geçirdiğinizden emin olun. Yazılım güvenliği bir süreçtir ve sürekli güncellenmelidir.

Bu yazıya tepkinizi paylaşın:
Mert Demir

Teknik beceriler ve bakım onarım rehberleri konusunda deneyimli bir editörüm. Okuyucularıma günlük hayatta tasarruf sağlayacak ipuçları sunuyorum.

Yorumlar (0)

Yorum Yaz