WordPress Arama Sonuçlarında Sadece Yazı Gösterme
WordPress kurulu bir siteyi yönetirken en sık karşılaşılan sorunlardan biri arama sonuçlarının karmaşık bir hal almasıdır. Kullanıcı bir şey aradığında karşısına yazılar, sayfalar, medya dosyaları, özel post tipleri ve hatta WooCommerce ürünleri çıkabilir. Bu durum özellikle içerik odaklı bloglarda gerçekten can sıkıcı bir hal alır. Bugün bu sorunu functions.php dosyasına ekleyeceğimiz birkaç fonksiyonla nasıl çözeceğimizi adım adım inceleyeceğiz.
Sorunun Kökü Nedir?
WordPress’in varsayılan arama mekanizması oldukça geniş kapsamlıdır. WP_Query sınıfı arama yaparken varsayılan olarak post_type parametresini any olarak ayarlar. Bu da şu anlama gelir: veritabanındaki tüm post tipleri arama kapsamına girer.
Bir düşünün, kullanıcı blog yazılarını ararken önüne “Gizlilik Politikası” sayfası veya bir ürün görseli çıkıyorsa bu hem kullanıcı deneyimini bozar hem de SEO açısından sorun yaratabilir. Arama motoru botları da sitenizin arama sayfalarını indekslediğinde tutarsız içerikle karşılaşır.
Gerçek dünya senaryosu: Diyelim ki bir teknoloji blogu yönetiyorsunuz. Sitenizde hem makaleler hem de bir “Hakkımızda”, “İletişim” gibi statik sayfalar var. Kullanıcı “Linux” kelimesini aradığında sonuçlarda makalelerin yanı sıra “Hakkımızda” sayfanız da çıkıyorsa bu arama deneyimini ciddi ölçüde bozar.
Temel Yaklaşım: pre_get_posts Kancası
WordPress’te arama davranışını değiştirmenin en temiz ve performanslı yolu pre_get_posts action kancasını kullanmaktır. Bu kanca, sorgu veritabanına gitmeden önce devreye girer ve sorguyu istediğiniz şekilde değiştirmenize olanak tanır.
// functions.php dosyasına ekleyin
function sadece_yazi_goster_aramada( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
$query->set( 'post_type', 'post' );
}
}
add_action( 'pre_get_posts', 'sadece_yazi_goster_aramada' );
Bu kısa kod parçası üç önemli kontrol yapıyor:
$query->is_search(): Mevcut sorgunun bir arama sorgusu olup olmadığını kontrol eder! is_admin(): Admin panelindeki aramaları etkilememesi için bu kontrolü ekliyoruz$query->is_main_query(): Sayfadaki ana sorguyu etkilemesini, yan sorgular veya widget sorgularını etkilememesini sağlar
Bu üç kontrolü birlikte kullanmak kritik önem taşır. Özellikle is_main_query() kontrolünü atlamak, sayfadaki diğer sorguları da etkileyerek beklenmedik sonuçlara yol açabilir.
Birden Fazla Post Tipi İzin Vermek
Sadece yazıları değil, belirli özel post tiplerini de arama sonuçlarına dahil etmek isteyebilirsiniz. Örneğin bir siteye hem blog yazıları hem de “proje” adında özel bir post tipi eklediğinizi düşünün.
function coklu_post_tipi_aramada( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
$query->set( 'post_type', array( 'post', 'proje', 'portfolio' ) );
}
}
add_action( 'pre_get_posts', 'coklu_post_tipi_aramada' );
Burada post_type parametresine bir dizi geçiriyoruz. Bu sayede “post” (yazılar), “proje” ve “portfolio” tipindeki içerikler arama sonuçlarına dahil edilirken sayfalar ve diğer tipler dışarıda kalıyor.
Belirli Kategorileri Aramadan Hariç Tutmak
Bazen daha ince ayarlar gerekir. Örneğin “Duyurular” kategorisindeki yazıları arama sonuçlarından çıkarmak isteyebilirsiniz. Bu tür içerikler genellikle zamana bağlı ve düşük değerli içeriklerdir.
function kategori_haric_arama( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
// Sadece yazıları göster
$query->set( 'post_type', 'post' );
// Duyurular kategorisini (ID: 5) hariç tut
$query->set( 'category__not_in', array( 5 ) );
}
}
add_action( 'pre_get_posts', 'kategori_haric_arama' );
Kategori ID’sini bilmiyorsanız, WordPress admin panelinde “Yazılar > Kategoriler” bölümüne gidin ve ilgili kategorinin düzenleme linkine tıklayın. URL’de tag_ID=5 gibi bir parametre göreceksiniz, bu sizin kategori ID’nizdir.
Yayımlanmış İçerikleri Filtrelemek
Arama sonuçlarında yalnızca yayımlanmış (published) yazıların çıkmasını garantilemek için post_status parametresini de ayarlayabilirsiniz.
function arama_filtrele_gelismis( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
// Sadece yazı tipini göster
$query->set( 'post_type', 'post' );
// Sadece yayımlanmış içerikleri göster
$query->set( 'post_status', 'publish' );
// Arama sonuçlarını tarihe göre sırala
$query->set( 'orderby', 'date' );
$query->set( 'order', 'DESC' );
// Sayfa başına maksimum sonuç sayısı
$query->set( 'posts_per_page', 10 );
}
}
add_action( 'pre_get_posts', 'arama_filtrele_gelismis' );
Bu yapı bir içerik blogu için ideal bir başlangıç noktasıdır. Her parametrenin ne işe yaradığını açıklayalım:
post_type => post: Sadece blog yazılarını kapsarpost_status => publish: Taslak veya bekleyen yazıları dışarıda bırakırorderby => date: En yeni içerikler önce gelirposts_per_page => 10: Sayfalama için sonuç sayısını sınırlar
WooCommerce Olan Sitelerde Dikkat Edilmesi Gerekenler
WooCommerce kullanan bir sitede bu filtreleme daha da kritik hale gelir. WooCommerce kendi post tiplerini ekler: product, product_variation, shop_order gibi. Bunların blog arama sonuçlarına karışması hem kullanıcıyı hem de site yöneticisini çıldırtabilir.
function woocommerce_arama_duzelt( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
// WooCommerce ürün arama sayfasında değilsek
if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
$query->set( 'post_type', 'post' );
}
}
}
add_action( 'pre_get_posts', 'woocommerce_arama_duzelt' );
Bu örnekte WooCommerce’e özel koşullu etiketleri kullanıyoruz. is_shop(), is_product_category() ve is_product_tag() fonksiyonları WooCommerce tarafından sağlanır ve kullanıcının mağaza alanında mı yoksa blog alanında mı olduğunu anlamanıza yardımcı olur.
Gerçek dünya senaryosu: Bir müşteri sitesi üzerinde çalışıyordunuz ve arama kutusuna “kırmızı” yazıldığında hem blog yazıları hem de 200 ürün çıkıyordu. Kullanıcılar kafayı yiyor, hemen çıkış yapıyordu. Bounce rate %78’e fırlamıştı. Bu iki satırlık düzenlemeyle sorunu çözdünüz ve bounce rate %45’e düştü.
Medya Dosyalarını Kesinlikle Aramadan Çıkarın
WordPress’te “attachment” post tipi medya dosyalarını temsil eder. Bu dosyalar arama sonuçlarında çıktığında gerçekten berbat görünür. Kullanıcı bir yazı ararken önüne “görsel-1024×768.jpg” gibi sonuçlar çıkmamalı.
function medya_aramayi_engelle( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
// Sadece yazıları dahil et, attachment'ları kesinlikle dışarıda bırak
$query->set( 'post_type', array( 'post' ) );
// Ek güvenlik olarak post_mime_type boş olanları getir
$query->set( 'post_mime_type', '' );
}
}
add_action( 'pre_get_posts', 'medya_aramayi_engelle' );
Özel Taksonomi ile Filtreleme
Bazı durumlarda sadece belirli etiketlere veya özel taksonomi terimlerine sahip yazıları arama sonuçlarında göstermek isteyebilirsiniz. Örneğin, etiketleri arasında “öne çıkan” olan yazıları önce göstermek gibi.
function etikete_gore_arama_sirala( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
$query->set( 'post_type', 'post' );
// Belirli bir etikete sahip yazıları üste çıkar
// Bu durumda 'one-cikan' slug'ına sahip etiketi kullanalım
$arama_terimi = get_search_query();
if ( ! empty( $arama_terimi ) ) {
// Meta değerine göre ek filtreleme
$query->set( 'meta_query', array(
'relation' => 'OR',
array(
'key' => '_featured_content',
'value' => '1',
'compare' => '='
),
array(
'key' => '_featured_content',
'compare' => 'NOT EXISTS'
)
) );
$query->set( 'orderby', array(
'meta_value' => 'DESC',
'date' => 'DESC'
) );
}
}
}
add_action( 'pre_get_posts', 'etikete_gore_arama_sirala' );
Arama Sonuçlarına Özel Alan (Custom Field) Filtrelemesi Eklemek
Belki de en güçlü kullanım senaryolarından biri özel alanlarla filtrelemedir. Diyelim ki yazılarınızda bir “zorluk seviyesi” meta alanı var ve kullanıcıların sadece “başlangıç” seviyesindeki yazıları aramasını istiyorsunuz.
function meta_alana_gore_arama( $query ) {
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
$query->set( 'post_type', 'post' );
// URL parametresinden zorluk seviyesini al
$zorluk = isset( $_GET['zorluk'] ) ? sanitize_text_field( $_GET['zorluk'] ) : '';
if ( ! empty( $zorluk ) && in_array( $zorluk, array( 'baslangic', 'orta', 'ileri' ) ) ) {
$query->set( 'meta_query', array(
array(
'key' => 'zorluk_seviyesi',
'value' => $zorluk,
'compare' => '='
)
) );
}
}
}
add_action( 'pre_get_posts', 'meta_alana_gore_arama' );
Bu sayede /search?s=linux&zorluk=baslangic gibi bir URL ile hem “linux” kelimesini arayan hem de “başlangıç” seviyesinde olan yazıları filtreleyebilirsiniz. Arama formunuza bir dropdown ekleyerek kullanıcıların bu filtreyi kolayca kullanmasını sağlayabilirsiniz.
Tüm Çözümleri Birleştiren Kapsamlı Versiyon
Yukarıdaki tüm yaklaşımları, gerçek bir prodüksiyon sitesinde kullanabileceğiniz tek bir temiz fonksiyona dönüştürelim:
/**
* WordPress arama sonuçlarını sadece yazıları gösterecek şekilde filtreler
* Versiyon: 1.0
* functions.php dosyasına ekleyin
*/
function optimize_arama_sorgsu( $query ) {
// Sadece ön yüz arama sorgularını etkile
if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
// WooCommerce aktifse ve mağaza sayfasındaysak müdahale etme
if ( function_exists( 'is_shop' ) && ( is_shop() || is_product_category() ) ) {
return;
}
// Sadece blog yazılarını göster
$query->set( 'post_type', array( 'post' ) );
// Sadece yayımlanmış içerikler
$query->set( 'post_status', 'publish' );
// Tarih sıralaması (en yeni önce)
$query->set( 'orderby', 'relevance' );
// Sayfa başına 10 sonuç
$query->set( 'posts_per_page', 10 );
// Şifre korumalı yazıları hariç tut
$query->set( 'has_password', false );
// URL'den kategori filtresi geliyorsa uygula
if ( isset( $_GET['cat'] ) && ! empty( $_GET['cat'] ) ) {
$cat_id = absint( $_GET['cat'] );
$query->set( 'cat', $cat_id );
}
// URL'den etiket filtresi geliyorsa uygula
if ( isset( $_GET['tag'] ) && ! empty( $_GET['tag'] ) ) {
$tag = sanitize_text_field( $_GET['tag'] );
$query->set( 'tag', $tag );
}
}
}
add_action( 'pre_get_posts', 'optimize_arama_sorgsu' );
Bu kapsamlı versiyon şunları yapıyor:
- Admin panelini korur:
! is_admin()kontrolü sayesinde admin aramalarına dokunmaz - Sadece ana sorguyu etkiler: Widget ve shortcode sorguları etkilenmez
- WooCommerce uyumludur: Mağaza sayfalarında devreye girmez
- Şifre korumalı yazıları gizler:
has_password => falseile güvenliği artırır - Kullanıcı filtrelerini destekler: GET parametreleriyle kategori ve etiket filtresi çalışır
- Güvenlik önlemleri içerir:
absint()vesanitize_text_field()ile veri temizlenir
Arama Sayfasında Sonuç Sayısını Göstermek
Filtreleme yaptıktan sonra kullanıcıya kaç sonuç bulunduğunu göstermek iyi bir UX pratiğidir. Bunu search.php şablon dosyanıza ekleyebilirsiniz ama functions.php üzerinden de bir yardımcı fonksiyon oluşturabilirsiniz:
/**
* Arama sonucu bilgisi döndürür
*/
function arama_sonuc_bilgisi() {
global $wp_query;
if ( ! is_search() ) {
return '';
}
$toplam = $wp_query->found_posts;
$arama = get_search_query();
$sayfa = max( 1, get_query_var( 'paged' ) );
$per_page = get_query_var( 'posts_per_page' );
$baslangic = ( ( $sayfa - 1 ) * $per_page ) + 1;
$bitis = min( $sayfa * $per_page, $toplam );
if ( $toplam === 0 ) {
return sprintf(
'<p class="arama-bilgi">"%s" için hiç sonuç bulunamadı.</p>',
esc_html( $arama )
);
}
return sprintf(
'<p class="arama-bilgi">"%s" için %d-%d arası gösteriliyor (toplam %d sonuç)</p>',
esc_html( $arama ),
$baslangic,
$bitis,
$toplam
);
}
Bu fonksiyonu search.php dosyanızda echo arama_sonuc_bilgisi(); şeklinde çağırabilirsiniz.
Yaygın Hatalar ve Çözümleri
Sysadmin olarak bu işi yaparken en çok karşılaşılan hatalar şunlardır:
is_admin()kontrolünü atlamak: Bunu yapmazsanız admin panelindeki arama da etkilenir ve yazı/sayfa bulamaz hale gelirsiniz. Çok sinir bozucu bir durum.
is_main_query()olmadan kullanmak: Sayfa yüklenirken birden fazla WP_Query çalışabilir (widgetlar, related posts pluginleri vs.). Bu kontrolü atlamak tüm bu sorguları bozar.
- Fonksiyon ismi çakışmaları: Eğer bir plugin zaten
sadece_yazi_goster_aramadaadında bir fonksiyon tanımlamışsa PHP fatal error alırsınız. Fonksiyon adlarınızı sitenize özgü prefix ile başlatın, örneğinmyblog_arama_filtrelegibi.
- Önbellek sorunu: Bu değişikliği yaptıktan sonra eğer bir caching plugin kullanıyorsanız (W3 Total Cache, WP Rocket vs.) önbelleği temizlemeyi unutmayın. Değişiklikler hemen yansımayabilir.
- Child theme kullanmamak: Doğrudan theme’nin
functions.phpdosyasını düzenlerseniz tema güncellemesinde tüm değişiklikleriniz gider. Mutlaka child theme kullanın.
Performans Açısından Değerlendirme
pre_get_posts ile yapılan bu düzenlemeler, plugin tabanlı çözümlere kıyasla çok daha hafiftir. Neden?
- Ek bir veritabanı sorgusu çalıştırmaz
- Mevcut sorguyu değiştirir, yeni bir sorgu oluşturmaz
- PHP’nin bellekte oluşturduğu nesne sayısını artırmaz
Ek WP_Query nesnesi oluşturarak veya posts_search filtresiyle SQL’i doğrudan manipüle ederek yapılan çözümler genellikle daha fazla kaynak tüketir. pre_get_posts bu iş için tasarlanmış doğru kancadır.
Sonuç
WordPress arama sonuçlarını sadece blog yazılarına kısıtlamak ilk bakışta basit bir görev gibi görünebilir ama doğru yapmak için birkaç önemli noktaya dikkat etmek gerekiyor. pre_get_posts kancası bu işin standardize edilmiş ve WordPress’in önerdiği yoludur. Plugin kullanmadan, sadece functions.php‘ye birkaç satır ekleyerek hem kullanıcı deneyimini hem de site performansını iyileştirebilirsiniz.
Üretim ortamında çalışmadan önce mutlaka bir staging ortamında test edin. WordPress güncellemelerinden etkilenmemek için child theme kullanın ve fonksiyon isimlerini sitenize özgü prefix ile başlatın. Bu küçük alışkanlıklar ilerleyen dönemde size saatlerce debug süresinden kurtarır.
Arama filtrelemesi sadece bir başlangıç noktasıdır; bu temeli kullanarak kullanıcılarınıza tarihe, kategoriye, etikete veya özel alanlara göre gelişmiş arama deneyimi sunabilirsiniz.
