WordPress’te Yazılara Otomatik Etiket Bulutu Ekleme

WordPress sitenizde içerik üretmek kadar o içeriği keşfedilebilir kılmak da önemli. Ziyaretçileriniz siteye geldiğinde ilgili yazılara kolayca ulaşabilmeli, etiketler arasında gezinebilmeli. İşte bu noktada etiket bulutu devreye giriyor. Ama standart WordPress widget’ı ile yetinmek zorunda değilsiniz. functions.php üzerinden özelleştirilmiş, akıllı bir etiket bulutu sistemi kurabilirsiniz.

Bu yazıda, WordPress’in yerleşik etiket sistemini functions.php fonksiyonlarıyla genişleterek yazılarınıza otomatik etiket bulutu eklemeyi ele alacağız. Hem tek yazı sayfalarında hem de arşiv sayfalarında çalışan, performanslı ve özelleştirilebilir bir yapı kuracağız.

Neden Özel Etiket Bulutu Sistemi?

WordPress’in varsayılan wp_tag_cloud() fonksiyonu işe yarar ama sınırlı. Herhangi bir yazının altına otomatik olarak ilgili etiketleri bir bulut şeklinde göstermez. Widget olarak sidebar’a ekleyebilirsiniz ama bu tüm etiketleri gösterir, yazıya özel değildir.

Gerçek dünya senaryosu düşünelim: 500 yazılık bir teknoloji blogunuz var. Her yazının altında o yazıyla ilgili etiketler gösterilsin, üstelik etiketin kaç yazıda kullanıldığına göre font boyutu değişsin, popüler etiketler daha büyük görünsün. Bunu WordPress panel’inden kutudan çıkar çıkmaz yapamazsınız. functions.php devreye giriyor.

Temel Yapı: İlk Fonksiyonumuz

Önce en basit halinden başlayalım. Bir yazının etiketlerini alıp HTML olarak döndüren bir fonksiyon yazalım:

// functions.php

function my_post_tag_cloud( $post_id = null ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }

    $tags = get_the_tags( $post_id );

    if ( ! $tags || is_wp_error( $tags ) ) {
        return '';
    }

    $output = '<div class="post-tag-cloud">';
    $output .= '<span class="tag-cloud-label">Etiketler: </span>';

    foreach ( $tags as $tag ) {
        $tag_link = get_tag_link( $tag->term_id );
        $output .= '<a href="' . esc_url( $tag_link ) . '" class="tag-cloud-link" rel="tag">';
        $output .= esc_html( $tag->name );
        $output .= '</a> ';
    }

    $output .= '</div>';

    return $output;
}

Bu temel fonksiyon, belirli bir yazının etiketlerini alıp link olarak döndürüyor. esc_url() ve esc_html() kullanmayı unutmayın, güvenlik her zaman önce gelir.

Otomatik Ekleme: the_content Hook

Şimdi bu fonksiyonu yazı içeriğinin altına otomatik ekleyelim. Her yazıya manuel olarak shortcode koymak yerine hook kullanacağız:

// functions.php

function auto_append_tag_cloud( $content ) {
    // Sadece tekil yazı sayfalarında çalışsın
    if ( ! is_single() || ! in_the_loop() || ! is_main_query() ) {
        return $content;
    }

    // Sadece 'post' post tipinde çalışsın
    if ( get_post_type() !== 'post' ) {
        return $content;
    }

    $tag_cloud = my_post_tag_cloud();

    if ( ! empty( $tag_cloud ) ) {
        $content .= $tag_cloud;
    }

    return $content;
}

add_filter( 'the_content', 'auto_append_tag_cloud' );

is_single(), in_the_loop() ve is_main_query() kontrollerini yapmak kritik. Aksi halde bu kod sidebar widget’larında, RSS feed’de ve beklenmedik yerlerde de çalışır.

Font Boyutunu Popülerliğe Göre Ayarlama

Gerçek bir etiket bulutu, etiketin kullanım sıklığına göre farklı boyutlarda görünür. Bu özelliği kendi fonksiyonumuza ekleyelim:

// functions.php

function my_weighted_tag_cloud( $post_id = null ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }

    $tags = get_the_tags( $post_id );

    if ( ! $tags || is_wp_error( $tags ) ) {
        return '';
    }

    // Tüm etiketlerin kullanım sayılarını al
    $tag_counts = array();
    foreach ( $tags as $tag ) {
        $tag_counts[ $tag->term_id ] = $tag->count;
    }

    // Min ve max değerleri bul
    $min_count = min( $tag_counts );
    $max_count = max( $tag_counts );

    // Font boyutu aralığı (px cinsinden)
    $min_font = 12;
    $max_font = 28;

    $output = '<div class="post-tag-cloud weighted">';
    $output .= '<span class="tag-cloud-title">Bu Yazının Etiketleri</span>';

    foreach ( $tags as $tag ) {
        $tag_link  = get_tag_link( $tag->term_id );
        $count     = $tag->count;

        // Font boyutunu hesapla
        if ( $max_count === $min_count ) {
            $font_size = ( $min_font + $max_font ) / 2;
        } else {
            $font_size = $min_font + ( ( $count - $min_count ) / ( $max_count - $min_count ) ) * ( $max_font - $min_font );
        }

        $font_size = round( $font_size, 1 );

        $output .= sprintf(
            '<a href="%s" class="tag-link tag-link-%d" style="font-size: %spx;" title="%d yazıda kullanıldı" rel="tag">%s</a>',
            esc_url( $tag_link ),
            (int) $tag->term_id,
            $font_size,
            (int) $count,
            esc_html( $tag->name )
        );
    }

    $output .= '</div>';

    return $output;
}

Bu fonksiyon, etiketin sitedeki toplam kullanım sayısını ($tag->count) baz alarak 12px ile 28px arasında bir font boyutu hesaplıyor. Matematiksel olarak linear interpolation kullanıyoruz.

Shortcode ile Kullanım

Bazen belirli sayfalara veya özel konumlara manuel olarak etiket bulutu eklemek isteyebilirsiniz. Bunun için bir shortcode oluşturalım:

// functions.php

function tag_cloud_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'post_id'  => null,
            'min_font' => 12,
            'max_font' => 28,
            'taxonomy' => 'post_tag',
        ),
        $atts,
        'etiket_bulutu'
    );

    $post_id = $atts['post_id'] ? (int) $atts['post_id'] : get_the_ID();

    $terms = wp_get_post_terms( $post_id, $atts['taxonomy'] );

    if ( is_wp_error( $terms ) || empty( $terms ) ) {
        return '<p class="no-tags">Bu yazı için etiket bulunmuyor.</p>';
    }

    $output = '<div class="shortcode-tag-cloud">';

    foreach ( $terms as $term ) {
        $term_link = get_term_link( $term );

        if ( is_wp_error( $term_link ) ) {
            continue;
        }

        $output .= '<a href="' . esc_url( $term_link ) . '" class="sc-tag-link">';
        $output .= esc_html( $term->name );
        $output .= ' <sup class="tag-count">(' . (int) $term->count . ')</sup>';
        $output .= '</a>';
    }

    $output .= '</div>';

    return $output;
}

add_shortcode( 'etiket_bulutu', 'tag_cloud_shortcode' );

Artık herhangi bir yazı veya sayfada [etiket_bulutu] yazarak etiket bulutunu gösterebilirsiniz. [etiket_bulutu post_id="42" taxonomy="category"] şeklinde parametreler de geçebilirsiniz. Üstelik bu shortcode kategoriler dahil herhangi bir taxonomy ile çalışıyor.

CSS ile Stil Verme

Fonksiyonları yazdık ama görsel taraf da önemli. Stilleri functions.php üzerinden inline olarak veya ayrı bir stylesheet olarak ekleyebiliriz. Küçük projeler için inline yaklaşım pratik:

// functions.php

function tag_cloud_inline_styles() {
    $css = '
        .post-tag-cloud {
            margin: 30px 0 20px;
            padding: 20px;
            background: #f8f9fa;
            border-left: 4px solid #0073aa;
            border-radius: 4px;
            line-height: 2.2;
        }

        .post-tag-cloud .tag-cloud-title {
            display: block;
            font-weight: 700;
            font-size: 14px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            color: #555;
            margin-bottom: 10px;
        }

        .post-tag-cloud a.tag-link,
        .post-tag-cloud a.sc-tag-link {
            display: inline-block;
            margin: 4px 6px;
            padding: 4px 12px;
            background: #fff;
            border: 1px solid #ddd;
            border-radius: 20px;
            color: #0073aa;
            text-decoration: none;
            transition: all 0.2s ease;
        }

        .post-tag-cloud a.tag-link:hover,
        .post-tag-cloud a.sc-tag-link:hover {
            background: #0073aa;
            color: #fff;
            border-color: #0073aa;
        }

        .tag-count {
            font-size: 10px;
            color: #999;
        }
    ';

    wp_add_inline_style( 'style', $css );
}

add_action( 'wp_enqueue_scripts', 'tag_cloud_inline_styles' );

wp_add_inline_style() fonksiyonu, stillerinizi mevcut tema stiline bağlı olarak ekler. 'style' yerine kendi tema stylesheet handle’ınızı kullanın.

Cache Desteği ile Performans Optimizasyonu

500+ yazılık bir sitede her sayfa yüklemesinde veritabanından etiketleri çekmek gereksiz yük oluşturur. Transient API ile cache ekleyelim:

// functions.php

function my_cached_tag_cloud( $post_id = null ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }

    // Cache key oluştur
    $cache_key = 'post_tag_cloud_' . (int) $post_id;

    // Önce cache'e bak
    $cached = get_transient( $cache_key );

    if ( false !== $cached ) {
        return $cached;
    }

    // Cache yoksa hesapla
    $tags = get_the_tags( $post_id );

    if ( ! $tags || is_wp_error( $tags ) ) {
        return '';
    }

    $output = '<div class="post-tag-cloud cached">';

    foreach ( $tags as $tag ) {
        $tag_link = get_tag_link( $tag->term_id );
        $output  .= '<a href="' . esc_url( $tag_link ) . '" class="tag-link" rel="tag">';
        $output  .= esc_html( $tag->name );
        $output  .= '</a>';
    }

    $output .= '</div>';

    // 24 saat cache'le
    set_transient( $cache_key, $output, DAY_IN_SECONDS );

    return $output;
}

// Yazı güncellenince cache'i temizle
function clear_tag_cloud_cache( $post_id ) {
    delete_transient( 'post_tag_cloud_' . (int) $post_id );
}

add_action( 'save_post', 'clear_tag_cloud_cache' );
add_action( 'set_object_terms', function( $object_id ) {
    delete_transient( 'post_tag_cloud_' . (int) $object_id );
});

Bu yapıda etiket bulutu ilk kez oluşturulduğunda 24 saat boyunca cache’leniyor. Yazı güncellendiğinde veya etiket değiştirildiğinde cache otomatik temizleniyor. Performans açısından ciddi fark yaratır.

İlgili Etiketlere Göre Yazı Önerisi Sistemi

Etiket bulutunu bir adım ileri taşıyalım. Mevcut yazının etiketlerine göre benzer yazıları listeleyen bir sistem kuralım. Bu, kullanıcı etkileşimini ciddi artırır:

// functions.php

function related_posts_by_tags( $post_id = null, $limit = 5 ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }

    $tags = wp_get_post_tags( $post_id, array( 'fields' => 'ids' ) );

    if ( empty( $tags ) ) {
        return '';
    }

    $cache_key = 'related_posts_' . $post_id;
    $cached    = get_transient( $cache_key );

    if ( false !== $cached ) {
        return $cached;
    }

    $related_query = new WP_Query( array(
        'tag__in'             => $tags,
        'post__not_in'        => array( $post_id ),
        'posts_per_page'      => (int) $limit,
        'orderby'             => 'rand',
        'post_status'         => 'publish',
        'ignore_sticky_posts' => true,
        'no_found_rows'       => true, // Performans için
    ) );

    if ( ! $related_query->have_posts() ) {
        return '';
    }

    $output  = '<div class="related-posts-by-tags">';
    $output .= '<h3 class="related-title">Benzer Yazılar</h3>';
    $output .= '<ul class="related-posts-list">';

    while ( $related_query->have_posts() ) {
        $related_query->the_post();
        $output .= '<li>';
        $output .= '<a href="' . esc_url( get_permalink() ) . '">';
        $output .= esc_html( get_the_title() );
        $output .= '</a>';
        $output .= ' <span class="related-date">' . get_the_date( 'd.m.Y' ) . '</span>';
        $output .= '</li>';
    }

    wp_reset_postdata();

    $output .= '</ul></div>';

    set_transient( $cache_key, $output, 12 * HOUR_IN_SECONDS );

    return $output;
}

// Bunu da the_content hook'una ekle
add_filter( 'the_content', function( $content ) {
    if ( is_single() && in_the_loop() && is_main_query() && get_post_type() === 'post' ) {
        $content .= my_cached_tag_cloud();
        $content .= related_posts_by_tags();
    }
    return $content;
} );

no_found_rows => true parametresi önemli. WordPress’in toplam sayfa sayısını hesaplamamasını sağlıyor, bu da gereksiz bir COUNT() sorgusu yapılmaması anlamına geliyor.

Custom Post Type Desteği

Pek çok site sadece standart yazı tipiyle çalışmıyor. Portfolyo, ürün, kurs gibi custom post type’larınız varsa bunlara da özel taxonomy desteği ekleyebilirsiniz:

// functions.php

function universal_term_cloud( $post_id = null, $taxonomies = null ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }

    $post_type = get_post_type( $post_id );

    // Varsayılan taxonomy haritası
    $default_taxonomies = array(
        'post'      => array( 'post_tag' ),
        'portfolio' => array( 'portfolio_tag', 'portfolio_category' ),
        'product'   => array( 'product_tag' ),
        'course'    => array( 'course_tag', 'difficulty' ),
    );

    if ( null === $taxonomies ) {
        $taxonomies = isset( $default_taxonomies[ $post_type ] )
            ? $default_taxonomies[ $post_type ]
            : array( 'post_tag' );
    }

    $all_terms = array();

    foreach ( $taxonomies as $taxonomy ) {
        $terms = wp_get_post_terms( $post_id, $taxonomy );

        if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
            $all_terms = array_merge( $all_terms, $terms );
        }
    }

    if ( empty( $all_terms ) ) {
        return '';
    }

    $output = '<div class="universal-term-cloud">';

    foreach ( $all_terms as $term ) {
        $term_link = get_term_link( $term );

        if ( is_wp_error( $term_link ) ) {
            continue;
        }

        $taxonomy_label = get_taxonomy( $term->taxonomy )->labels->singular_name;

        $output .= sprintf(
            '<a href="%s" class="term-link tax-%s" data-taxonomy="%s" title="%s kategorisinden">%s</a>',
            esc_url( $term_link ),
            esc_attr( $term->taxonomy ),
            esc_attr( $term->taxonomy ),
            esc_attr( $taxonomy_label ),
            esc_html( $term->name )
        );
    }

    $output .= '</div>';

    return $output;
}

Bu fonksiyon, hangi post type’ta olduğunuzu algılıyor ve o post type’a uygun taxonomy’leri otomatik seçiyor. WooCommerce ürünleri için product_tag, portfolyo için portfolio_tag gibi.

Admin Paneli: Toplu Etiket İstatistikleri

Son olarak, admin panelinde etiket kullanım istatistiklerini gösteren küçük bir araç ekleyelim. Bu, içerik stratejinizi şekillendirmenize yardım eder:

// functions.php

function tag_statistics_dashboard_widget() {
    $tags = get_tags( array(
        'orderby'    => 'count',
        'order'      => 'DESC',
        'number'     => 10,
        'hide_empty' => false,
    ) );

    if ( empty( $tags ) ) {
        echo '<p>Henüz etiket yok.</p>';
        return;
    }

    echo '<ul style="margin:0; padding:0; list-style:none;">';

    foreach ( $tags as $tag ) {
        $edit_link = get_edit_term_link( $tag->term_id, 'post_tag' );
        $bar_width = ( $tag->count / $tags[0]->count ) * 100;

        printf(
            '<li style="margin-bottom:10px;">
                <a href="%s" style="font-weight:600; color:#0073aa;">%s</a>
                <span style="float:right; color:#666; font-size:12px;">%d yazı</span>
                <div style="background:#e0e0e0; height:4px; border-radius:2px; margin-top:4px;">
                    <div style="background:#0073aa; width:%d%%; height:4px; border-radius:2px;"></div>
                </div>
            </li>',
            esc_url( $edit_link ),
            esc_html( $tag->name ),
            (int) $tag->count,
            (int) $bar_width
        );
    }

    echo '</ul>';
}

function register_tag_stats_widget() {
    wp_add_dashboard_widget(
        'tag_statistics_widget',
        'En Çok Kullanılan Etiketler (Top 10)',
        'tag_statistics_dashboard_widget'
    );
}

add_action( 'wp_dashboard_setup', 'register_tag_stats_widget' );

Bu kod, WordPress dashboard’unuza “En Çok Kullanılan Etiketler” widget’ı ekler. Her etiketin kaç yazıda kullanıldığını görmek, hangi konularda daha fazla içerik üretmeniz gerektiğini anlamanıza yardım eder.

Sık Yapılan Hatalar

Etiket bulutu uygulamalarında karşılaşılan yaygın sorunları listeleyelim:

  • in_the_loop() kontrolü yapmamak: Hook’u her yerde çalıştırırsınız, sidebar ve footer’da da görünür
  • wp_reset_postdata() unutmak: Custom WP_Query sonrası global $post değişkeni bozulur, büyük sorunlara yol açar
  • Cache key çakışması: Farklı post type’lar için aynı cache key formatını kullanmak karışıklık yaratır, key’e post type ekleyin
  • Taxonomy doğrulaması yapmamak: wp_get_post_terms() çağrısından önce taxonomy’nin var olup olmadığını kontrol edin
  • XSS açıkları: Kullanıcıdan gelen verileri esc_html() ve esc_url() ile temizlemeden çıktılamak güvenlik riski oluşturur

Sonuç

functions.php üzerinden kurulan etiket bulutu sistemi, hem kullanıcı deneyimini hem de SEO’yu doğrudan etkiler. Ziyaretçiler ilgili içeriklere kolayca ulaşırken, arama motorları da içerikler arasındaki ilişkileri daha iyi anlıyor.

Bu yazıda ele aldığımız yapıyı özetleyecek olursak:

  • Temel etiket fonksiyonu: Her yazının etiketlerini alıp HTML döndürür
  • the_content hook: Otomatik ekleme için en temiz yöntem
  • Ağırlıklı bulutu: Font boyutunu kullanım sıklığına göre ayarlar
  • Shortcode: Esnek ve elle kontrol edilebilir ekleme
  • Transient cache: Performansı korur
  • İlgili yazılar: Etiketlerden yararlanarak kullanıcı etkileşimini artırır
  • Dashboard widget: İçerik stratejinize veri sağlar

Projenizin büyüklüğüne göre bu parçaları birleştirebilir veya sadece ihtiyacınız olanı alabilirsiniz. Küçük bir blog için sadece temel fonksiyon ve the_content hook yeterli olabilir. Büyük bir içerik sitesi için cache, ilgili yazılar ve dashboard widget’ı birlikte kullanmak mantıklı.

Son bir not: Bu kodları functions.php‘ye eklemeden önce mutlaka bir child theme kullanın. Ana temanın güncellemesi fonksiyonlarınızı silecektir.

Bir yanıt yazın

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