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_length filtresiyle kelime sayısı belirlenir
  • excerpt_more filtresiyle son ek ([...]) belirlenir
  • the_excerpt filtresiyle 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">&rarr;</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   .= ' &bull; ';
        $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_length filtresini kullanın, önceliği 999 yapın
  • “Devamını Oku” linki için excerpt_more filtresine dokunun
  • Türkçe içerikler için mutlaka mb_* string fonksiyonlarını tercih edin
  • Yüksek trafikli sitelerde transient cache kullanın, save_post ile cache temizlemeyi unutmayın
  • WooCommerce ürünleri için woocommerce_short_description filtresini kullanın
  • the_excerpt ile get_the_excerpt arası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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir