Arama Sorgusunu Filtreleme: WordPress’te pre_get_posts Kullanımı
WordPress ile çalışırken en sık karşılaşılan sorunlardan biri, sitenin arama fonksiyonunun tam olarak istediğin gibi davranmamasıdır. Belki arama sonuçlarına sadece belirli post type’ları dahil etmek istiyorsun, belki belirli kategorileri dışlamak ya da arama sorgusuna ekstra parametreler eklemek gerekiyor. İşte tam bu noktada pre_get_posts hook’u devreye giriyor ve hayatını kurtarıyor.
pre_get_posts Nedir ve Neden Kullanmalısın?
pre_get_posts, WordPress’in veritabanına sorgu göndermeden hemen önce tetiklenen bir action hook’tur. Bu hook sayesinde WP_Query nesnesini doğrudan değiştirme imkanı bulursun. Yani WordPress “veritabanından şunları getireceğim” diye karar verdikten sonra ama sorguyu göndermeden önce araya girip “hayır, şunları getir” diyebilirsin.
Neden bu kadar önemli? Çünkü alternatifler genellikle ya verimsiz ya da hatalara açık. Örneğin query_posts() fonksiyonunu kullanmak global sorguyu bozar, performans sorunlarına yol açar ve sayfalama gibi özellikleri kırar. WP_Query ile yeni sorgu oluşturmak ise ana sorguyu değiştirmez. pre_get_posts ise ana sorguyu doğrudan, temiz ve verimli bir şekilde değiştirme imkanı tanır.
Hook’un Temel Yapısı
function sysadmin_custom_query( $query ) {
// Admin panelinde ve ana sorgu dışında çalışmasını engelle
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
// Buraya sorgu değişikliklerini ekle
}
add_action( 'pre_get_posts', 'sysadmin_custom_query' );
Bu yapıda iki kritik kontrol var:
- is_admin(): Admin panelindeki sorgular etkilenmesin diye bu kontrolü her zaman koy. Yoksa admin listelerini de değiştirirsin ve büyük karışıklık çıkar.
- $query->is_main_query(): Sadece ana sorguyu değiştir, sidebar widget’ları veya başka WP_Query örneklerini değil.
Bu iki kontrolü atlarsan sonuçlar felaket olabilir. Bunu öğrenmenin en kolay yolu maalesef production sitede bu hatayı yapmak oluyor, o yüzden peşinen söyliyeyim.
Temel Senaryo: Arama Sorgusunu Filtreleme
En yaygın kullanım senaryosu, arama sonuçlarını özelleştirmektir. Varsayılan olarak WordPress arama sorgusu tüm post type’ları, tüm kategorileri ve tüm içeriği kapsar. Bu çoğu zaman istemediğin sonuçları getirir.
Yalnızca Belirli Post Type’ları Aramaya Dahil Etme
Diyelim ki bir WooCommerce sitesi yönetiyorsun ve kullanıcılar arama yaptığında sadece ürünlerin ve blog yazılarının çıkmasını istiyorsun. Sayfalar, özel post type’lar vs. arama sonuçlarında görünmesin.
function sysadmin_search_post_types( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() ) {
$query->set( 'post_type', array( 'post', 'product' ) );
}
}
add_action( 'pre_get_posts', 'sysadmin_search_post_types' );
Bu kadar basit. $query->set() metodu ile istediğin parametreyi değiştirebilirsin. Burada post_type parametresini bir array olarak geçiyoruz ve sadece post ile product tiplerinin arama sonuçlarına dahil edilmesini sağlıyoruz.
Belirli Kategorileri Arama Sonuçlarından Çıkarma
Bazen bazı kategorileri arama sonuçlarından tamamen dışlamak gerekir. Örneğin “Duyurular” kategorisindeki yazılar hiç arama sonucunda görünmesin istiyorsun.
function sysadmin_exclude_categories_search( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() ) {
// Dışlamak istediğin kategori ID'lerini buraya yaz
// Negatif değer = dışla anlamına gelir
$query->set( 'cat', '-5,-12,-23' );
}
}
add_action( 'pre_get_posts', 'sysadmin_exclude_categories_search' );
Kategori ID’lerinin önüne eksi işareti koyarak o kategorileri dışlıyorsun. Birden fazla kategori için virgülle ayırarak devam edebilirsin. Kategori ID’lerini öğrenmek için WordPress admin panelinde Yazılar > Kategoriler’e git, üzerine gelince URL’de tag_ID=X şeklinde görürsün.
Gelişmiş Senaryo: Taxonomy ile Filtreleme
Sadece kategori ve etiket değil, özel taxonomy’lerle de filtreleme yapabilirsin. Bu özellikle WooCommerce’de ürün kategorileri ile çalışırken çok işe yarıyor.
function sysadmin_search_exclude_product_cat( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() ) {
$tax_query = array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => array( 'kampanya-disi', 'arsiv-urunler' ),
'operator' => 'NOT IN',
),
);
$query->set( 'tax_query', $tax_query );
}
}
add_action( 'pre_get_posts', 'sysadmin_search_exclude_product_cat' );
Burada tax_query kullanıyoruz. operator değerini NOT IN olarak ayarlayarak belirtilen taxonomy term’lerini içeren postları dışlıyoruz. IN kullansan sadece o kategorilerdeki ürünleri getirirsin.
Performans Optimizasyonu: Sayfa Başına Sonuç Sayısı
Ana sayfa ve arşiv sayfaları için farklı sayfa başına yazı sayısı ayarlamak da pre_get_posts ile yapılır.
function sysadmin_posts_per_page( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
// Arama sayfasında sayfa başına 20 sonuç göster
if ( $query->is_search() ) {
$query->set( 'posts_per_page', 20 );
return;
}
// Kategori arşivlerinde sayfa başına 12 yazı göster
if ( $query->is_category() ) {
$query->set( 'posts_per_page', 12 );
return;
}
// Ana sayfada sadece 6 yazı göster
if ( $query->is_home() ) {
$query->set( 'posts_per_page', 6 );
}
}
add_action( 'pre_get_posts', 'sysadmin_posts_per_page' );
Bu yaklaşımla farklı sayfa tipleri için farklı sayfalama ayarları yapabilirsin. WordPress ayarlarından “Sayfa başına yazı sayısı”nı değiştirmek global bir değişiklik yapar, ama pre_get_posts ile granüler kontrol sağlarsın.
Meta Query ile Özel Alan Filtreleme
Gerçek dünyada en sık ihtiyaç duyduğun senaryolardan biri, özel alanlara göre arama yapmak. Örneğin bir emlak sitesi yönetiyorsun ve kullanıcı arama yaptığında sadece “aktif” durumundaki ilanlar çıksın istiyorsun.
function sysadmin_search_active_listings( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() && is_user_logged_in() === false ) {
// Giriş yapmamış kullanıcılara sadece aktif ve onaylı ilanları göster
$meta_query = array(
'relation' => 'AND',
array(
'key' => 'listing_status',
'value' => 'active',
'compare' => '=',
),
array(
'key' => 'is_approved',
'value' => '1',
'compare' => '=',
),
);
$query->set( 'meta_query', $meta_query );
}
}
add_action( 'pre_get_posts', 'sysadmin_search_active_listings' );
relation parametresi birden fazla meta koşulunu nasıl birleştireceğini belirler:
- AND: Tüm koşullar sağlanmalı
- OR: En az bir koşul sağlanmalı
Sıralama Düzenini Değiştirme
Arama sonuçlarını varsayılan olarak “alakalılık” sırasına göre değil, farklı kriterlere göre sıralamak isteyebilirsin. Belki tarih sırası, belki özel bir alana göre sıralama gerekiyor.
function sysadmin_custom_search_orderby( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() ) {
// Önce öne çıkarılmış yazılar, sonra tarihe göre azalan
$query->set( 'orderby', array(
'meta_value_num' => 'DESC',
'date' => 'DESC',
) );
$query->set( 'meta_key', 'featured_score' );
}
}
add_action( 'pre_get_posts', 'sysadmin_custom_search_orderby' );
Burada orderby için array kullanıyoruz. Bu WordPress 4.0 ile birlikte gelen bir özellik ve çok kullanışlı. Önce meta_value_num (sayısal meta değeri) baz alınıyor, eşit durumda tarih devreye giriyor.
WooCommerce Özel Senaryosu: Ürün Aramasını Özelleştirme
WooCommerce kullanan sitelerde ürün araması için özel ihtiyaçlar ortaya çıkıyor. Örneğin stokta olmayan ürünlerin arama sonuçlarında görünmemesi gerekebilir.
function sysadmin_exclude_out_of_stock( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
// Sadece WooCommerce ürün araması için
if ( $query->is_search() && isset( $query->query_vars['post_type'] ) ) {
// WooCommerce stok yönetimi aktifse
if ( get_option( 'woocommerce_hide_out_of_stock_items' ) === 'yes' ) {
$meta_query = $query->get( 'meta_query' );
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
$meta_query[] = array(
'key' => '_stock_status',
'value' => 'outofstock',
'compare' => '!=',
);
$query->set( 'meta_query', $meta_query );
}
}
}
add_action( 'pre_get_posts', 'sysadmin_exclude_out_of_stock' );
Dikkat ettiğin üzere burada mevcut meta_query varsa onu alıp üzerine ekliyoruz. Direkt $query->set() ile yeni bir array atamak yerine mevcut sorguya ekleme yapıyoruz, çünkü başka bir filtre zaten meta_query eklemiş olabilir. Bu yaklaşım daha güvenli ve diğer eklentilerle uyumlu çalışır.
Kullanıcı Rolüne Göre Sonuçları Filtreleme
Bazen farklı kullanıcı gruplarına farklı arama sonuçları göstermen gerekir. Admin’ler her şeyi görsün, aboneler sadece yayınlanmış içerikleri görsün gibi.
function sysadmin_role_based_search( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() ) {
$current_user = wp_get_current_user();
// Kullanıcı giriş yapmamışsa sadece public içerik
if ( ! is_user_logged_in() ) {
$query->set( 'post_status', 'publish' );
$query->set( 'cat', '-99' ); // VIP kategori ID'si
return;
}
// Abone rolündeyse
if ( in_array( 'subscriber', $current_user->roles ) ) {
$query->set( 'post_status', 'publish' );
return;
}
// Editor ve üzeri için tüm statüsleri göster
if ( current_user_can( 'edit_others_posts' ) ) {
$query->set( 'post_status', array( 'publish', 'pending', 'draft' ) );
}
}
}
add_action( 'pre_get_posts', 'sysadmin_role_based_search' );
Bu senaryoda current_user_can() ve wp_get_current_user() kullanarak kullanıcı yeteneklerine ve rollerine göre sorguyu şekillendiriyoruz.
Debug: Sorgunun Ne Yaptığını Görmek
pre_get_posts ile çalışırken zaman zaman sorgunun gerçekte ne yaptığını görmek istersin. Bu noktada WordPress’in debug araçları devreye giriyor.
function sysadmin_debug_query( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_search() && WP_DEBUG && current_user_can( 'manage_options' ) ) {
// Sorgu değişkenlerini göster (sadece admin kullanıcılara)
add_action( 'wp_footer', function() use ( $query ) {
echo '<pre style="background:#000;color:#0f0;padding:20px;margin:20px;font-size:12px;">';
echo 'Query Vars: ';
print_r( $query->query_vars );
echo '</pre>';
});
}
}
add_action( 'pre_get_posts', 'sysadmin_debug_query' );
Bu debug kodu sadece WP_DEBUG aktifken ve sadece admin yetkisine sahip kullanıcılar için çalışır. Production’a geçmeden önce bu kodu kaldırmayı unutma.
Alternatif olarak $query->request property’sini kullanarak oluşan SQL sorgusunu da görebilirsin, ama bu sadece sorgu çalıştıktan sonra erişilebilir olduğu için pre_get_posts içinde değil, the_posts gibi daha sonraki bir hook’ta kullanman gerekir.
Sık Yapılan Hatalar ve Çözümleri
pre_get_posts kullanırken belirli hataları çok sık görüyorum. Bunları bilerek işe girişirsen zaman kaybetmezsin.
Hata 1: is_main_query() kontrolünü atlamak
Bu kontrolü atlarsan sidebar’daki recent posts widget’ı, related posts gibi tüm yan sorgular da etkilenir. Site yavaşlar ve beklenmedik içerikler görünür.
Hata 2: is_admin() kontrolünü atlamak
Admin paneli sorgularını etkilersen, post listelerinde garip davranışlar görürsün. Bazı postlar silinmiş gibi görünür, aramalar çalışmaz.
Hata 3: Sorgu değişkenlerini okumak için $query->get() yerine global $wp_query kullanmak
pre_get_posts içinde global $wp_query henüz tam olarak hazır değildir. Parametre okumak için $query->get( 'parametre_adi' ) kullan.
Hata 4: Mevcut meta_query’yi silmek
Yeni meta_query ataması yaparken mevcut olanı korumayı unutmak, diğer eklentilerin eklediği koşulları silerek beklenmedik sonuçlara yol açar.
// YANLIS yontem - mevcut meta_query'yi siler
$query->set( 'meta_query', array(
array( 'key' => 'my_key', 'value' => 'my_value' )
) );
// DOGRU yontem - mevcut meta_query'yi koru ve ekle
$existing_meta_query = $query->get( 'meta_query' );
if ( ! is_array( $existing_meta_query ) ) {
$existing_meta_query = array();
}
$existing_meta_query[] = array( 'key' => 'my_key', 'value' => 'my_value' );
$query->set( 'meta_query', $existing_meta_query );
functions.php’ye Eklerken Dikkat Edilmesi Gerekenler
Tüm bu kodları functions.php‘ye eklerken bazı pratik noktalara dikkat etmek gerekiyor.
- Çocuk tema kullan: Parent tema güncellendiğinde tüm özelleştirmelerini kaybedersin. Mutlaka child theme oluştur ve oraya ekle.
- Fonksiyon isimlerini benzersiz yap: Prefix kullan, örneğin
sysadmin_veya site adına göre bir prefix. Eklentilerle çakışma riskini minimuma indirirsin. - Kodun hatalara karşı dayanıklı olmasını sağla: Fonksiyon başında gerekli kontrolleri yap, boş değer kontrolü ekle.
- Tek bir pre_get_posts fonksiyonu kullan: Birden fazla küçük fonksiyon eklemek yerine, tüm koşulları tek bir iyi organize edilmiş fonksiyonda topla. Bu hem performans hem de okunabilirlik açısından daha iyi.
function sysadmin_main_query_modifier( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
// Arama sorgusu ayarları
if ( $query->is_search() ) {
$query->set( 'post_type', array( 'post', 'product', 'page' ) );
$query->set( 'posts_per_page', 20 );
$query->set( 'post_status', 'publish' );
return; // Arama için gerekli ayarlar yapıldı, devam etme
}
// Kategori arşiv ayarları
if ( $query->is_category() ) {
$query->set( 'posts_per_page', 12 );
$query->set( 'orderby', 'date' );
$query->set( 'order', 'DESC' );
return;
}
// Ana sayfa ayarları
if ( $query->is_home() ) {
$query->set( 'posts_per_page', 6 );
$query->set( 'cat', '-5' ); // Belirli kategoriyi hariç tut
}
}
add_action( 'pre_get_posts', 'sysadmin_main_query_modifier' );
Bu şekilde tek bir hook içinde tüm koşulları yönetmek, kodun bakımını kolaylaştırır ve hangi hook’un ne zaman çalıştığını takip etmeyi basitleştirir.
Sonuç
pre_get_posts, WordPress geliştirme dünyasında belki de en güçlü ve en sık kullanılan hook’lardan biri. Doğru kullanıldığında sorgu maliyetini düşürür, kullanıcı deneyimini geliştirir ve siteyi tam istediğin gibi davranmaya zorlarsın.
Özetlemek gerekirse:
- Her zaman
is_admin()ve$query->is_main_query()kontrollerini ekle - Değer okumak için
$query->get(), değer yazmak için$query->set()kullan - Mevcut meta_query ve tax_query değerlerini koruyarak üstüne ekle
- Tüm sorgu değişikliklerini tek bir fonksiyonda topla
- Production’a almadan önce debug modunda test et
Bu hook’u bir kez düzgün öğrendin mi, WordPress arama ve arşiv sorguları ile ilgili neredeyse her problemi çözebilirsin. Eklenti kurmak yerine birkaç satır kod yazarak hem daha hafif hem de daha kontrollü bir çözüm elde edersin. Sysadmin bakış açısından bu tür minimal, verimli çözümler her zaman tercih edilmesi gereken yaklaşımdır.
