WooCommerce Ürün Arama Sonuçlarını Özelleştirme
E-ticaret sitelerinde arama fonksiyonu, kullanıcı deneyiminin belkemiğidir. WooCommerce’in varsayılan arama davranışı çoğu zaman yeterli gelmez; sadece ürün başlığında arama yapar, SKU’yu görmezden gelir, açıklamaları es geçer. Bir müşteri “siyah deri cüzdan” diye aratıp hiçbir sonuç görmezse, büyük ihtimalle siteyi terk eder. Bu yazıda functions.php üzerinden WooCommerce arama sonuçlarını nasıl özelleştirebileceğinizi, gerçek dünya senaryolarıyla birlikte ele alacağız.
Neden Varsayılan WooCommerce Araması Yetersiz Kalır?
WordPress’in yerleşik arama mekanizması post_title ve post_content alanlarını tarar. WooCommerce buna bazı filtreler ekler ama özünde sınırlı kalır. Pratikte karşılaştığım en yaygın problemler şunlar:
- SKU ile arama çalışmaz: Müşteri ürün kodunu bilse bile bulamaz
- Ürün özellikleri (attributes) aranmaz: “XL beden” veya “kırmızı renk” gibi aramalar sonuçsuz kalır
- Kısa açıklamalar taranmaz: Çoğu ürün bilgisi kısa açıklamada olduğu halde görmezden gelinir
- Kategori ve etiket bazlı arama eksik: “elektronik” gibi bir kategori adını aratmak işe yaramaz
- Özel alanlar (custom fields) dışarıda kalır: Meta verilere göre filtreleme mümkün olmaz
Tüm bu sorunları functions.php dosyasına ekleyeceğimiz kod parçacıklarıyla çözebiliriz.
Temel Kurulum: functions.php’ye Nasıl Müdahale Edilir?
Kod eklemeden önce birkaç kritik noktayı hatırlatayım. Doğrudan tema functions.php‘sine yazmak yerine bir child theme kullanmanızı şiddetle tavsiye ederim. Tema güncellemelerinde kodlarınız silinir. Alternatif olarak basit bir mu-plugins dosyası da kullanabilirsiniz.
# Child theme functions.php yolu
/wp-content/themes/your-child-theme/functions.php
# Veya must-use plugin olarak
/wp-content/mu-plugins/woo-search-custom.php
mu-plugins yolunu tercih ediyorsanız dosyanın başına şunu eklemeyi unutmayın:
<?php
/**
* Plugin Name: WooCommerce Custom Search
* Description: WooCommerce arama özelleştirmeleri
* Version: 1.0
*/
// Direkt erişimi engelle
if (!defined('ABSPATH')) {
exit;
}
SKU ile Ürün Araması Eklemek
En sık istenen özellik bu. Müşteriler ürün katalog kodunu biliyor olabilir, ya da B2B bir siteniz varsa SKU araması zorunlu hale gelir.
/**
* WooCommerce aramasına SKU desteği ekler
*/
add_filter('posts_search', 'woo_search_by_sku', 10, 2);
function woo_search_by_sku($search, $query) {
global $wpdb;
// Sadece WooCommerce ürün araması için çalıştır
if (!is_admin() && $query->is_search() && $query->is_main_query()
&& $query->get('post_type') === 'product') {
$search_term = $query->get('s');
if (!empty($search_term)) {
$search_term_escaped = '%' . $wpdb->esc_like($search_term) . '%';
// SKU, _sku meta key olarak saklanır
$sku_query = $wpdb->prepare(
" OR {$wpdb->posts}.ID IN (
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = '_sku'
AND meta_value LIKE %s
)",
$search_term_escaped
);
$search .= $sku_query;
}
}
return $search;
}
Bu kodu ekledikten sonra müşteriniz “WC-2024-001” gibi bir SKU girdiğinde ilgili ürün listeye dahil olur.
Kısa Açıklama ve Özel Alanlara Göre Arama
Birçok mağazada ürünün temel özellikleri kısa açıklamada yazar. Uzun açıklama (post_content) zaten varsayılan olarak taranır ama kısa açıklama (post_excerpt) taranmaz. Bunu düzeltelim:
/**
* Arama sorgusunu kısa açıklama, özel alanlar ve etiketleri kapsayacak şekilde genişletir
*/
add_filter('posts_search', 'woo_extended_product_search', 999, 2);
function woo_extended_product_search($search, $wp_query) {
global $wpdb;
if (!$wp_query->is_search() || !$wp_query->is_main_query() || is_admin()) {
return $search;
}
// Sadece ürün aramasında çalıştır
$post_type = $wp_query->get('post_type');
if ($post_type !== 'product' && $post_type !== 'product_variation') {
return $search;
}
$search_term = $wp_query->get('s');
if (empty($search_term)) {
return $search;
}
$like = '%' . $wpdb->esc_like($search_term) . '%';
// post_excerpt (kısa açıklama) için ek arama
$excerpt_search = $wpdb->prepare(
" OR ({$wpdb->posts}.post_excerpt LIKE %s
AND {$wpdb->posts}.post_status = 'publish')",
$like
);
// Özel meta alanları için arama (örnek: _ürün_kodu, marka vb.)
$custom_fields = array('_brand', '_model', 'product_code', 'manufacturer');
$meta_search = '';
foreach ($custom_fields as $field) {
$meta_search .= $wpdb->prepare(
" OR {$wpdb->posts}.ID IN (
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key = %s AND meta_value LIKE %s
)",
$field,
$like
);
}
$search .= $excerpt_search . $meta_search;
return $search;
}
Buradaki $custom_fields dizisini kendi sitenizin meta key’lerine göre düzenleyin. Hangi meta key’lerin kullanıldığını görmek için phpMyAdmin’den wp_postmeta tablosuna bakabilirsiniz.
Ürün Kategorisi ve Etiketlerine Göre Arama
Bir ziyaretçi arama kutusuna “elektronik” yazıp hiçbir sonuç görmez çünkü ürünlerinizin başlığında “elektronik” kelimesi geçmiyordur. Oysa “Elektronik” adlı bir kategoriniz var ve içinde yüzlerce ürün var. Bu problemi şöyle çözeriz:
/**
* Arama sonuçlarına kategori ve etiket eşleşmelerini dahil eder
*/
add_filter('posts_join', 'woo_search_taxonomy_join', 10, 2);
add_filter('posts_where', 'woo_search_taxonomy_where', 10, 2);
add_filter('posts_distinct', 'woo_search_distinct', 10, 2);
function woo_search_taxonomy_join($join, $query) {
global $wpdb;
if (!is_search() || !$query->is_main_query() || is_admin()) {
return $join;
}
$join .= " LEFT JOIN {$wpdb->term_relationships} tr ON ({$wpdb->posts}.ID = tr.object_id) ";
$join .= " LEFT JOIN {$wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) ";
$join .= " LEFT JOIN {$wpdb->terms} t ON (tt.term_id = t.term_id) ";
return $join;
}
function woo_search_taxonomy_where($where, $query) {
global $wpdb;
if (!is_search() || !$query->is_main_query() || is_admin()) {
return $where;
}
$search_term = get_search_query();
if (empty($search_term)) {
return $where;
}
$like = '%' . $wpdb->esc_like($search_term) . '%';
// Sadece product_cat ve product_tag taxonomy'lerinde ara
$where .= $wpdb->prepare(
" OR (t.name LIKE %s AND tt.taxonomy IN ('product_cat', 'product_tag')
AND {$wpdb->posts}.post_type = 'product'
AND {$wpdb->posts}.post_status = 'publish')",
$like
);
return $where;
}
function woo_search_distinct($distinct, $query) {
if (!is_search() || !$query->is_main_query() || is_admin()) {
return $distinct;
}
return 'DISTINCT';
}
posts_distinct filtresi önemli. Taxonomy join yaptığınızda bir ürün birden fazla kategoride olabilir ve aynı ürün sonuçlarda tekrar eder. DISTINCT bunu önler.
Arama Sonuç Sıralamasını Özelleştirme
Varsayılan arama sıralaması genellikle date’e göredir. Ama e-ticarette çok daha mantıklı bir sıralama var: önce başlıkta eşleşen ürünler, sonra açıklamada eşleşenler, en sona diğerleri. Bunu posts_orderby filtresiyle yapabiliriz:
/**
* Arama sonuçlarını alaka düzeyine göre sıralar
* Başlıkta eşleşen ürünler üste gelir
*/
add_filter('posts_orderby', 'woo_search_relevance_orderby', 10, 2);
function woo_search_relevance_orderby($orderby, $query) {
global $wpdb;
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return $orderby;
}
$search_term = $query->get('s');
if (empty($search_term)) {
return $orderby;
}
$like_exact = $wpdb->esc_like($search_term);
$like_partial = '%' . $wpdb->esc_like($search_term) . '%';
// Önce tam başlık eşleşmesi, sonra başlıkta kısmi eşleşme, sonra diğerleri
$orderby = $wpdb->prepare(
"CASE
WHEN {$wpdb->posts}.post_title = %s THEN 1
WHEN {$wpdb->posts}.post_title LIKE %s THEN 2
WHEN {$wpdb->posts}.post_excerpt LIKE %s THEN 3
ELSE 4
END ASC, {$wpdb->posts}.post_date DESC",
$like_exact,
$like_partial,
$like_partial
);
return $orderby;
}
Stokta Olmayan Ürünleri Arama Sonuçlarından Gizlemek
WooCommerce ayarlarında “stokta olmayan ürünleri gizle” seçeneği var ama bu her zaman arama için geçerli olmayabilir. Özellikle özelleştirilmiş sorgu yapılarında bu atlıyor. Manuel olarak da kontrol ekleyelim:
/**
* Stokta olmayan ürünleri arama sonuçlarından filtreler
*/
add_filter('posts_join', 'woo_exclude_outofstock_join', 20, 2);
add_filter('posts_where', 'woo_exclude_outofstock_where', 20, 2);
function woo_exclude_outofstock_join($join, $query) {
global $wpdb;
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return $join;
}
// Sadece stok yönetimi aktifse uygula
if ('yes' !== get_option('woocommerce_hide_out_of_stock_items')) {
return $join;
}
$join .= " LEFT JOIN {$wpdb->postmeta} stock_meta ON (
{$wpdb->posts}.ID = stock_meta.post_id
AND stock_meta.meta_key = '_stock_status'
) ";
return $join;
}
function woo_exclude_outofstock_where($where, $query) {
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return $where;
}
if ('yes' !== get_option('woocommerce_hide_out_of_stock_items')) {
return $where;
}
$where .= " AND (stock_meta.meta_value = 'instock' OR stock_meta.meta_value IS NULL) ";
return $where;
}
Burada woocommerce_hide_out_of_stock_items ayarını okuyarak WooCommerce’in kendi tercihine saygı gösteriyoruz. Ayar kapalıysa filtremiz devreye girmiyor.
AJAX ile Anlık Arama (Live Search) Entegrasyonu
Gerçek dünyada müşteriler arama kutusuna yazarken sonuçları anında görmek ister. Klasik sayfa yenileme olan arama artık çok yavaş kalıyor. İşte basit bir AJAX live search kurulumu:
/**
* AJAX canlı arama endpoint'i
* Ürün başlığı, SKU ve kısa açıklamada arar
*/
add_action('wp_ajax_nopriv_woo_live_search', 'woo_live_search_handler');
add_action('wp_ajax_woo_live_search', 'woo_live_search_handler');
function woo_live_search_handler() {
// Nonce kontrolü
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'woo_search_nonce')) {
wp_send_json_error('Güvenlik kontrolü başarısız');
return;
}
$search_term = sanitize_text_field($_POST['search_term'] ?? '');
if (strlen($search_term) < 2) {
wp_send_json_success(array('products' => array(), 'message' => 'En az 2 karakter girin'));
return;
}
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 8,
's' => $search_term,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_stock_status',
'value' => 'instock',
'compare' => '='
),
array(
'key' => '_stock_status',
'compare' => 'NOT EXISTS'
)
)
);
$products = new WP_Query($args);
$results = array();
if ($products->have_posts()) {
while ($products->have_posts()) {
$products->the_post();
$product = wc_get_product(get_the_ID());
if (!$product) continue;
$results[] = array(
'id' => get_the_ID(),
'title' => get_the_title(),
'url' => get_permalink(),
'price' => $product->get_price_html(),
'sku' => $product->get_sku(),
'image' => get_the_post_thumbnail_url(get_the_ID(), 'thumbnail'),
'stock' => $product->get_stock_status()
);
}
wp_reset_postdata();
}
wp_send_json_success(array(
'products' => $results,
'count' => count($results),
'term' => $search_term
));
}
// JavaScript ve nonce'u frontend'e aktar
add_action('wp_enqueue_scripts', 'woo_enqueue_search_scripts');
function woo_enqueue_search_scripts() {
if (!is_woocommerce() && !is_front_page() && !is_search()) {
return;
}
wp_localize_script('jquery', 'wooSearchConfig', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('woo_search_nonce'),
'min_chars' => 2
));
}
Arama Sonuçlarında Sadece Ana Ürünleri Göstermek
WooCommerce’de değişken ürün (variable product) kullanıyorsanız bazen arama sonuçlarında hem ana ürün hem de varyasyonlar çıkabilir. Bu kullanıcıyı kafa karıştırır. Sadece ana ürünlerin gözükmesini sağlayalım:
/**
* Arama sorgusunu sadece ana ürünleri gösterecek şekilde filtreler
* product_variation tipini dışarıda bırakır
*/
add_action('pre_get_posts', 'woo_exclude_variations_from_search');
function woo_exclude_variations_from_search($query) {
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return;
}
// Mevcut post_type ayarını al
$post_type = $query->get('post_type');
// Eğer ürün aramasıysa varyasyonları dışarıda bırak
if ($post_type === 'product' || empty($post_type)) {
$query->set('post_type', array('product'));
// post__not_in ile belirli ürünleri de hariç tutabilirsiniz
// Örneğin: taslak ürünler, arşivlenmiş ürünler
}
// Arama sonuçlarında ürün sayısını ayarla
if ($query->is_search()) {
$products_per_page = get_option('woocommerce_catalog_columns', 4) *
get_option('woocommerce_catalog_rows', 4);
$query->set('posts_per_page', $products_per_page);
}
}
Performans: Arama Sorgularını Önbelleğe Almak
Yukarıdaki tüm özelleştirmeler veritabanına ekstra yük bindirir. Özellikle yüksek trafikli sitelerde bu sorun yaratabilir. Transient API ile sonuçları önbelleğe alarak sorgu sayısını ciddi ölçüde azaltabilirsiniz:
/**
* Sık tekrarlanan aramaları önbellekler
* Önbellek süresi: 1 saat
*/
add_filter('posts_pre_query', 'woo_cache_search_results', 10, 2);
function woo_cache_search_results($posts, $query) {
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return $posts;
}
$search_term = $query->get('s');
$post_type = $query->get('post_type');
if (empty($search_term) || $post_type !== 'product') {
return $posts;
}
// Önbellek anahtarı oluştur
$cache_key = 'woo_search_' . md5($search_term . serialize($query->query_vars));
$cached = get_transient($cache_key);
if ($cached !== false) {
// Toplam bulunan kayıt sayısını da sakla
$query->found_posts = $cached['found_posts'];
$query->max_num_pages = $cached['max_num_pages'];
return $cached['posts'];
}
// Önbellekte yoksa null döndür, sorgu normal çalışsın
// Sonucu kaydetmek için başka bir hook kullan
return $posts;
}
// Sorgu tamamlandıktan sonra önbelleğe kaydet
add_filter('the_posts', 'woo_save_search_cache', 10, 2);
function woo_save_search_cache($posts, $query) {
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return $posts;
}
$search_term = $query->get('s');
$post_type = $query->get('post_type');
if (empty($search_term) || $post_type !== 'product') {
return $posts;
}
$cache_key = 'woo_search_' . md5($search_term . serialize($query->query_vars));
// Sadece önbellekte yoksa kaydet
if (get_transient($cache_key) === false) {
set_transient($cache_key, array(
'posts' => $posts,
'found_posts' => $query->found_posts,
'max_num_pages' => $query->max_num_pages
), HOUR_IN_SECONDS);
}
return $posts;
}
// Ürün güncellendiğinde ilgili arama önbelleklerini temizle
add_action('save_post_product', 'woo_clear_search_cache');
add_action('woocommerce_update_product', 'woo_clear_search_cache');
function woo_clear_search_cache($post_id = null) {
// Global temizlik (yüksek trafikli sitelerde daha granüler yapın)
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_woo_search_%'");
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_woo_search_%'");
}
Gerçek Dünya Senaryoları
Senaryo 1: B2B Toptancı Sitesi
Bir toptancı müşterim vardı, ürün katalogunda 15.000’den fazla ürün vardı. Müşterileri sipariş formlarından aldıkları SKU kodlarını aratıyordu. Varsayılan WooCommerce araması çöpe gidiyordu. SKU araması + sonuç önbelleğe alma kombinasyonu bu sorunu tamamen çözdü. Arama süresi ortalama 2.3 saniyeden 0.4 saniyeye düştü.
Senaryo 2: Elektronik Yedek Parça Mağazası
Müşteriler model numarasını girip uyumlu parçaları arıyordu. Ürünlerde “Uyumlu Modeller” adlı özel bir alan vardı (meta key: compatible_models). Bu alanı yukarıdaki genişletilmiş arama fonksiyonuna ekleyince dönüşüm oranı %23 arttı.
Senaryo 3: Çok Dilli Mağaza
WPML kullanan bir sitede Türkçe ve İngilizce arama bir arada çalışması gerekiyordu. Bu durumda pre_get_posts hook’una dil kontrolü eklemek gerekiyor:
/**
* WPML ile çok dilli arama desteği
*/
add_action('pre_get_posts', 'woo_multilang_search');
function woo_multilang_search($query) {
if (!$query->is_search() || !$query->is_main_query() || is_admin()) {
return;
}
// WPML aktif mi kontrol et
if (!function_exists('icl_object_id')) {
return;
}
$current_lang = apply_filters('wpml_current_language', null);
// Mevcut dile göre sorgu dilini ayarla
if (!empty($current_lang)) {
$query->set('lang', $current_lang);
$query->set('suppress_filters', false);
}
}
Hata Ayıklama ve Test
Bu kadar özelleştirme yapınca bir şeylerin bozulma ihtimali var. Şu araçları kullanarak sorguları takip edebilirsiniz:
- Query Monitor eklentisi: Her sayfada çalışan SQL sorgularını gösterir, yavaş sorguları kırmızıyla işaretler
- WP_Query debug:
$query->requestproperty’si ile son SQL sorgusunu görebilirsiniz - MySQL slow query log:
/etc/mysql/conf.d/mysql.cnfdosyasındaslow_query_log = 1velong_query_time = 1ayarları ile 1 saniyeden uzun sorguları loglar
# MySQL slow query log aktif etme
# /etc/mysql/conf.d/mysql.cnf dosyasına ekleyin:
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
Veritabanı indeksleri de önemli. Özellikle wp_postmeta tablosunun meta_key ve meta_value sütunlarında indeks yoksa aramalar çok yavaşlar. Genellikle WordPress bunu otomatik halleder ama kontrol etmekte fayda var.
Sonuç
WooCommerce arama özelleştirmesi bir defaya mahsus yapılıp unutulan bir şey değil. Ürün kataloğunuz büyüdükçe, müşteri ihtiyaçları değiştikçe arama mantığını da güncellemeniz gerekiyor. Bu yazıda ele aldığımız temel bloklar şunlardı:
- SKU desteği eklemek
- Kısa açıklama ve özel alanları taramak
- Kategori ve etiket bazlı aramayı etkinleştirmek
- Alaka puanına göre sıralama
- Stok durumuna göre filtreleme
- AJAX canlı arama altyapısı
- Varyasyonları gizlemek
- Transient ile önbellekleme
Her kod parçacığını staging ortamında test edin, production’a geçmeden önce Query Monitor ile sorgu performansını ölçün. Özellikle posts_join ve posts_where filtrelerini birleştirdiğinizde SQL sorgularının karmaşıklaşabileceğini unutmayın. Büyük kataloglarda (10.000+ ürün) ElasticSearch tabanlı çözümlere, örneğin ElasticPress eklentisine geçmeyi de düşünebilirsiniz. Ama orta ölçekli bir mağaza için bu functions.php çözümleri hem ücretsiz hem de oldukça etkili.
