WordPress Excerpt Filtreleme ve Özelleştirme
WordPress ile çalışırken excerpt (özet) alanı, genellikle göz ardı edilen ama aslında SEO ve kullanıcı deneyimi açısından kritik bir bileşendir. Özellikle liste sayfalarında, kategori arşivlerinde veya ana sayfada gösterilen yazı özetleri, ziyaretçinin o yazıya tıklayıp tıklamamasını doğrudan etkiler. Varsayılan WordPress excerpt davranışı çoğu zaman yetersiz kalır: karakter sınırı yok, HTML temizleme tutarsız, “Devamını Oku” linki özelleştirilemez. Bugün bunların hepsini functions.php üzerinden çözeceğiz.
WordPress Excerpt Nasıl Çalışır?
WordPress’te iki tür excerpt vardır. Birincisi manuel excerpt: yazar, yazı editöründe “Özet” kutusuna elle bir metin girer. İkincisi otomatik excerpt: eğer manuel özet girilmemişse WordPress, yazı içeriğinin ilk 55 kelimesini alır ve sonuna [...] ekler.
the_excerpt() fonksiyonu çağrıldığında WordPress şu sırayla işlem yapar:
- Önce manuel özet var mı kontrol eder
- Yoksa
wp_trim_excerpt()ile otomatik özet oluşturur excerpt_lengthfiltresiyle kelime sayısı belirlenirexcerpt_morefiltresiyle son ek ([...]) belirlenirthe_excerptfiltresiyle son çıktı işlenir
Bu akışı anladıktan sonra, her noktaya müdahale etmek son derece kolay hale gelir.
Excerpt Uzunluğunu Değiştirmek
En temel ihtiyaçtan başlayalım. Varsayılan 55 kelime, çoğu tema için ya çok fazla ya da çok az olabiliyor. Bunu değiştirmek için excerpt_length filtresini kullanıyoruz.
// functions.php
function custom_excerpt_length( $length ) {
return 30; // 30 kelime ile sınırla
}
add_filter( 'excerpt_length', 'custom_excerpt_length', 999 );
Burada öncelik değeri olarak 999 kullanmamın nedeni önemli: bazı temalar ve eklentiler de bu filtreyi kullanıyor. Yüksek öncelik vermek, bizim fonksiyonumuzun en son çalışmasını sağlar ve diğerlerini ezer.
Peki ya farklı post type’lar için farklı uzunluk istiyorsak? Örneğin portföy yazıları için 20 kelime, blog yazıları için 40 kelime. Global $post nesnesinden post type bilgisini okuyabiliriz:
function dynamic_excerpt_length( $length ) {
global $post;
if ( isset( $post->post_type ) ) {
switch ( $post->post_type ) {
case 'portfolio':
return 20;
case 'product':
return 25;
case 'post':
return 40;
default:
return 55;
}
}
return $length;
}
add_filter( 'excerpt_length', 'dynamic_excerpt_length', 999 );
Bu yaklaşım özellikle WooCommerce ürünleri veya özel post type’lar içeren sitelerde hayat kurtarıyor.
“Devamını Oku” Linkini Özelleştirmek
Varsayılan [...] görünümü hem işlevsiz hem de çirkin. Ziyaretçi bu karakterlerin ne anlama geldiğini anlamayabilir. Bunu excerpt_more filtresiyle istediğimiz bir HTML yapısına dönüştürebiliriz.
function custom_excerpt_more( $more ) {
global $post;
$read_more_link = '<a class="read-more-link" href="' . get_permalink( $post->ID ) . '">
Devamını Oku <span class="arrow">→</span>
</a>';
return '... ' . $read_more_link;
}
add_filter( 'excerpt_more', 'custom_excerpt_more' );
CSS tarafında .read-more-link sınıfına buton stilini uygulayabilirsiniz. Bu şekilde hem estetik hem de fonksiyonel bir “Devamını Oku” butonu elde edersiniz.
Ancak dikkat edilmesi gereken bir nokta var: excerpt_more filtresi sadece otomatik excerptlerde çalışır. Manuel özet girilmişse bu filtre tetiklenmez. Bunu aşmak için biraz farklı bir yaklaşım gerekir, bunu ilerleyen bölümde ele alacağız.
HTML İçeren Excerpt Oluşturmak
WordPress varsayılan olarak excerpt’teki tüm HTML etiketlerini temizler. Bazen bu istenen bir davranışken, bazen excerpt içinde kalın yazı, italik veya liste görmek isteriz. get_the_excerpt filtresini kullanarak bunu kontrol altına alabiliriz.
function excerpt_with_allowed_html( $excerpt ) {
// Şu an excerpt modunda olup olmadığımızı kontrol edelim
if ( is_admin() ) {
return $excerpt;
}
$post_id = get_the_ID();
$raw_excerpt = get_post_field( 'post_excerpt', $post_id );
// Manuel özet varsa ve HTML içeriyorsa, izin verilen taglarla döndür
if ( ! empty( $raw_excerpt ) ) {
$allowed_tags = '<p><strong><em><ul><ol><li><a>';
return wp_kses( $raw_excerpt, array(
'p' => array(),
'strong' => array(),
'em' => array(),
'ul' => array( 'class' => array() ),
'ol' => array( 'class' => array() ),
'li' => array(),
'a' => array( 'href' => array(), 'title' => array(), 'class' => array() ),
));
}
return $excerpt;
}
add_filter( 'get_the_excerpt', 'excerpt_with_allowed_html' );
wp_kses() fonksiyonu WordPress’in güvenlik fonksiyonlarından biridir. Sadece belirttiğiniz HTML etiketlerine ve özelliklerine izin verir, diğerlerini temizler. XSS saldırılarına karşı güvenli bir yaklaşımdır.
Karakter Tabanlı Excerpt Kısaltma
Kelime sayısı yerine karakter sayısına göre kısaltma yapmak istediğinizde biraz daha özel bir fonksiyon yazmanız gerekiyor. Bu özellikle Türkçe ve diğer dillerde daha tutarlı sonuçlar verir, çünkü Türkçe’de kelime uzunlukları İngilizce’ye göre oldukça farklı olabiliyor.
function get_excerpt_by_chars( $post_id = null, $char_limit = 200, $more = '...' ) {
if ( ! $post_id ) {
$post_id = get_the_ID();
}
// Önce manuel özeti kontrol et
$excerpt = get_post_field( 'post_excerpt', $post_id );
// Manuel özet yoksa içerikten al
if ( empty( $excerpt ) ) {
$excerpt = get_post_field( 'post_content', $post_id );
$excerpt = strip_shortcodes( $excerpt );
$excerpt = wp_strip_all_tags( $excerpt );
$excerpt = str_replace( array( "rn", "r", "n" ), ' ', $excerpt );
$excerpt = preg_replace( '/s+/', ' ', $excerpt );
$excerpt = trim( $excerpt );
}
// Karakter sınırını uygula
if ( mb_strlen( $excerpt ) > $char_limit ) {
$excerpt = mb_substr( $excerpt, 0, $char_limit );
// Son kelimeyi kesmemek için son boşluğa kadar al
$last_space = mb_strrpos( $excerpt, ' ' );
if ( $last_space !== false ) {
$excerpt = mb_substr( $excerpt, 0, $last_space );
}
$excerpt .= $more;
}
return $excerpt;
}
Bu fonksiyon doğrudan template’de şöyle kullanılır:
// Template dosyasında kullanım
echo get_excerpt_by_chars( get_the_ID(), 150, '... <a href="' . get_permalink() . '">Devamını oku</a>' );
mb_strlen() ve mb_substr() fonksiyonları kullandığımıza dikkat edin. Türkçe karakterler (ş, ğ, ü, ö, ç, ı) gibi çok baytlı karakterleri doğru saymak için multibyte string fonksiyonlarını kullanmak şart. Normal strlen() Türkçe karakterleri yanlış sayar.
WooCommerce Ürün Excerpt Özelleştirme
WooCommerce, ürün sayfalarında the_excerpt() fonksiyonunu kısa açıklama olarak kullanır. Ürün listelerinde ise excerpt’i farklı şekilde işler. Özellikle mağaza sayfasında ürün açıklamalarını belirli bir uzunlukla göstermek istediğinizde bu fonksiyonu kullanabilirsiniz:
function woocommerce_product_short_description_limit( $post_excerpt ) {
global $post;
// Sadece WooCommerce ürünleri için uygula
if ( ! isset( $post->post_type ) || $post->post_type !== 'product' ) {
return $post_excerpt;
}
// Shop sayfası veya arşiv sayfasındaysak kısalt
if ( is_shop() || is_product_category() || is_product_tag() ) {
$limit = 100; // karakter
$clean_excerpt = wp_strip_all_tags( $post_excerpt );
if ( mb_strlen( $clean_excerpt ) > $limit ) {
$clean_excerpt = mb_substr( $clean_excerpt, 0, $limit );
$last_space = mb_strrpos( $clean_excerpt, ' ' );
if ( $last_space !== false ) {
$clean_excerpt = mb_substr( $clean_excerpt, 0, $last_space );
}
return '<p>' . $clean_excerpt . '...</p>';
}
return $post_excerpt;
}
// Ürün detay sayfasında tam özeti göster
return $post_excerpt;
}
add_filter( 'woocommerce_short_description', 'woocommerce_product_short_description_limit' );
Bu filtre woocommerce_short_description‘a bağlı. WooCommerce’in kendi filtresini kullanmak, standart get_the_excerpt filtresini kullanmaktan daha güvenli çünkü sadece ürün bağlamında tetikleniyor.
Excerpt’e Schema Markup Eklemek
SEO açısından excerpt’lere schema markup eklemek, Google’ın içeriği daha iyi anlamasına yardımcı olur. Blog yazıları için description özelliği ekleyelim:
function add_schema_to_excerpt( $excerpt ) {
// Admin panelinde, feed'de veya REST API'de schema ekleme
if ( is_admin() || is_feed() || ( defined('REST_REQUEST') && REST_REQUEST ) ) {
return $excerpt;
}
// Boş excerpt ise dokunma
if ( empty( $excerpt ) ) {
return $excerpt;
}
$post_id = get_the_ID();
$clean_text = wp_strip_all_tags( $excerpt );
$clean_text = esc_attr( $clean_text );
// Excerpt container'ına itemprop ekle
return '<div itemprop="description">' . $excerpt . '</div>';
}
add_filter( 'the_excerpt', 'add_schema_to_excerpt' );
Dikkat: Bu fonksiyonu the_excerpt filtresine bağladık, get_the_excerpt‘e değil. the_excerpt() template tag’i çağrıldığında tetiklenir. get_the_excerpt() ise raw değeri döndürür. Hangisini kullandığınıza göre doğru filtreyi seçin.
Excerpt Cache Mekanizması
Büyük sitelerde, her sayfa yüklemesinde excerpt hesaplaması performansı etkileyebilir. Özellikle yukarıdaki gibi regex ve string işlemleri yapılan özel fonksiyonlar varsa, sonuçları transient API ile cache’lemek mantıklıdır:
function cached_custom_excerpt( $post_id = null, $length = 200 ) {
if ( ! $post_id ) {
$post_id = get_the_ID();
}
$cache_key = 'custom_excerpt_' . $post_id . '_' . $length;
$cached = get_transient( $cache_key );
if ( $cached !== false ) {
return $cached;
}
// Excerpt hesapla
$post = get_post( $post_id );
$excerpt = ! empty( $post->post_excerpt ) ? $post->post_excerpt : $post->post_content;
$excerpt = strip_shortcodes( $excerpt );
$excerpt = wp_strip_all_tags( $excerpt );
$excerpt = preg_replace( '/s+/', ' ', trim( $excerpt ) );
if ( mb_strlen( $excerpt ) > $length ) {
$excerpt = mb_substr( $excerpt, 0, $length );
$last_space = mb_strrpos( $excerpt, ' ' );
if ( $last_space !== false ) {
$excerpt = mb_substr( $excerpt, 0, $last_space );
}
$excerpt .= '...';
}
// 12 saat cache'le
set_transient( $cache_key, $excerpt, 12 * HOUR_IN_SECONDS );
return $excerpt;
}
// Yazı güncellendiğinde cache'i temizle
function clear_excerpt_cache( $post_id ) {
// Tüm olası uzunluklar için cache'i temizle
$lengths = array( 100, 150, 200, 250, 300 );
foreach ( $lengths as $length ) {
delete_transient( 'custom_excerpt_' . $post_id . '_' . $length );
}
}
add_action( 'save_post', 'clear_excerpt_cache' );
add_action( 'edit_post', 'clear_excerpt_cache' );
Bu pattern, save_post ve edit_post action’larına bağlanarak yazı güncellendiğinde cache otomatik temizleniyor. Çok ziyaret alan sitelerde bu küçük detay fark yaratır.
Excerpt’i Shortcode Destekli Hale Getirmek
Bazı durumlarda excerpt içinde shortcode çalıştırmak gerekebilir. Özellikle sayfa oluşturucular (Elementor, Divi) kullanan sitelerde bu ihtiyaç ortaya çıkıyor.
function excerpt_with_shortcode_support( $excerpt ) {
// Sadece ön yüzde uygula
if ( is_admin() ) {
return $excerpt;
}
// Shortcode varsa çalıştır
if ( has_shortcode( $excerpt, 'your_shortcode' ) ) {
return do_shortcode( $excerpt );
}
// Genel shortcode desteği istiyorsanız
// DİKKAT: Güvenilmeyen içeriklerde tüm shortcode'ları çalıştırmak riskli olabilir
// return do_shortcode( $excerpt );
return $excerpt;
}
add_filter( 'the_excerpt', 'excerpt_with_shortcode_support' );
Burada önemli bir güvenlik notu: do_shortcode() fonksiyonunu sadece güvendiğiniz, siz veya editörlerinizin girdiği içeriklere uygulayın. Kullanıcı tarafından girilen excerpt’lerde do_shortcode() çalıştırmak XSS vektörü oluşturabilir.
Gerçek Dünya Senaryosu: Haber Sitesi Excerpt Yönetimi
Bir müşteri için haber sitesi geliştirdiğimde şöyle bir ihtiyaç çıkmıştı: Ana sayfada öne çıkan yazılar için 300 karakter, kategori listelerinde 150 karakter, sidebar widget’larında 80 karakter excerpt gösterilecekti. Üstelik excerpt’te yazar adı ve tarih bilgisi de yer alacaktı.
Bunun için get_template_part() ile farklı context’ler oluşturmak yerine, önce bir utility fonksiyonu yazdım:
/**
* Bağlama göre excerpt döndürür.
*
* @param string $context 'featured' | 'archive' | 'widget'
* @param int $post_id
* @return string
*/
function get_contextual_excerpt( $context = 'archive', $post_id = null ) {
if ( ! $post_id ) {
$post_id = get_the_ID();
}
$config = array(
'featured' => array( 'length' => 300, 'show_meta' => true ),
'archive' => array( 'length' => 150, 'show_meta' => false ),
'widget' => array( 'length' => 80, 'show_meta' => false ),
);
$settings = isset( $config[ $context ] ) ? $config[ $context ] : $config['archive'];
// Cache kontrol
$cache_key = 'ctx_excerpt_' . $post_id . '_' . $context;
$cached = get_transient( $cache_key );
if ( $cached !== false ) {
return $cached;
}
$post = get_post( $post_id );
$excerpt = ! empty( $post->post_excerpt ) ? $post->post_excerpt : $post->post_content;
$excerpt = wp_strip_all_tags( strip_shortcodes( $excerpt ) );
$excerpt = preg_replace( '/s+/', ' ', trim( $excerpt ) );
$length = $settings['length'];
if ( mb_strlen( $excerpt ) > $length ) {
$excerpt = mb_substr( $excerpt, 0, $length );
$last_space = mb_strrpos( $excerpt, ' ' );
if ( $last_space !== false ) {
$excerpt = mb_substr( $excerpt, 0, $last_space );
}
$excerpt .= '...';
}
$output = '<p class="excerpt excerpt--' . esc_attr( $context ) . '">' . esc_html( $excerpt ) . '</p>';
if ( $settings['show_meta'] ) {
$author_id = get_post_field( 'post_author', $post_id );
$output .= '<p class="excerpt-meta">';
$output .= '<span class="author">' . esc_html( get_the_author_meta( 'display_name', $author_id ) ) . '</span>';
$output .= ' • ';
$output .= '<time datetime="' . get_the_date( 'c', $post_id ) . '">' . get_the_date( '', $post_id ) . '</time>';
$output .= '</p>';
}
set_transient( $cache_key, $output, 6 * HOUR_IN_SECONDS );
return $output;
}
Template dosyasında kullanım şöyle:
// Ana sayfa featured yazıda
echo get_contextual_excerpt( 'featured', get_the_ID() );
// Kategori arşivinde
echo get_contextual_excerpt( 'archive' );
// Sidebar widget'ında
echo get_contextual_excerpt( 'widget', $post->ID );
Bu yaklaşım sayesinde excerpt mantığı tek bir yerde yönetildi, template dosyaları gereksiz PHP kodundan temizlendi.
Yaygın Hatalar ve Çözümleri
Excerpt çalışırken karşılaşılan bazı sık hatalar var:
the_excerpt ve get_the_excerpt farkını karıştırmak: the_excerpt() direkt ekrana basar ve the_excerpt filtresini tetikler. get_the_excerpt() değeri döndürür ve get_the_excerpt filtresini tetikler. Filtrenizi doğru hook’a bağladığınızdan emin olun.
Loop dışında excerpt almak: the_excerpt() sadece WordPress loop içinde doğru çalışır. Loop dışında excerpt almak için get_post_field( 'post_excerpt', $post_id ) kullanın ve gerekirse manuel hesaplama yapın.
Türkçe karakter sorunları: Daha önce vurguladım ama tekrar belirteyim: strlen() yerine mb_strlen(), substr() yerine mb_substr(), strrpos() yerine mb_strrpos() kullanın. WordPress varsayılan olarak UTF-8 kullanır ve multibyte fonksiyonlar zorunludur.
Çift filtre uygulaması: Birden fazla excerpt filtresi eklediğinizde öncelik sıralarına dikkat edin. Aynı hook’a bağlı iki fonksiyon çakışabilir. Her zaman öncelik parametresini açıkça belirtin.
WooCommerce ile çatışma: WooCommerce bazı filtreleri kendisi de kullanıyor. woocommerce_short_description filtresi ve the_excerpt filtresi bazen çakışabiliyor. Ürün sayfaları için her zaman WooCommerce’in kendi filtrelerini tercih edin.
Sonuç
WordPress excerpt yönetimi, ilk bakışta basit görünse de gerçek dünya projelerinde oldukça karmaşık bir hal alabilir. excerpt_length ve excerpt_more filtreleri temel ihtiyaçları karşılar. Ancak çok post type içeren siteler, WooCommerce mağazaları veya farklı bağlamlarda farklı özet uzunlukları gereken projeler için özelleştirilmiş fonksiyonlar yazmak kaçınılmaz oluyor.
Özetlemek gerekirse:
- Kelime sınırı için
excerpt_lengthfiltresini kullanın, önceliği999yapın - “Devamını Oku” linki için
excerpt_morefiltresine dokunun - Türkçe içerikler için mutlaka
mb_*string fonksiyonlarını tercih edin - Yüksek trafikli sitelerde transient cache kullanın,
save_postile cache temizlemeyi unutmayın - WooCommerce ürünleri için
woocommerce_short_descriptionfiltresini kullanın the_excerptileget_the_excerptarasındaki farkı aklınızda tutun
Bu fonksiyonların tamamını tek bir functions.php dosyasına koymak yerine, inc/excerpt-functions.php gibi ayrı bir dosyada toplayıp functions.php‘den require_once ile dahil etmenizi tavsiye ederim. Hem düzenli kalır hem de tema güncellemelerinde kaybolma riski azalır.
