WordPress Like Butonu Ekleme: Üyelik Gerektirmeden

WordPress sitene üyelik sistemi kurmak, kullanıcıların içeriklerle etkileşim kurmasının önündeki en büyük engellerden biri. Ziyaretçinin bir yazıyı beğenmesi için kayıt olmasını zorunlu tutmak, dönüşüm oranlarını ciddi şekilde düşürüyor. Oysa basit bir “Like” butonu, kullanıcı deneyimini iyileştirmenin ve içerik performansını ölçmenin en kolay yollarından biri. Bu yazıda, herhangi bir eklenti kullanmadan ve üyelik gerektirmeden çalışan bir like butonu sistemini tamamen functions.php ile nasıl kuracağını adım adım anlatacağım.

Temel Mantık ve Yaklaşım

Üyelik gerektirmeyen bir beğeni sistemi için iki temel yöntem var: cookie tabanlı ve IP tabanlı kontrol. Cookie yöntemi, kullanıcının tarayıcısına bir çerez yazarak aynı kişinin tekrar beğenmesini engeller. IP tabanlı yöntem ise sunucu tarafında IP adresini kaydeder. Her ikisinin de artıları ve eksileri var.

Cookie yöntemi daha kullanıcı dostudur çünkü tarayıcı geçmişi temizlenirse veya farklı bir tarayıcı kullanılırsa tekrar beğenilebilir. Bu, çoğu site için kabul edilebilir bir durum. IP tabanlı yöntem ise daha katıdır ama VPN kullananları veya paylaşımlı ağları (kafeterya, ofis) yanlış değerlendirebilir.

Bu yazıda cookie tabanlı yöntemi kullanacağız çünkü:

  • GDPR açısından daha yönetilebilir
  • Sunucu tarafında ekstra veritabanı sorgusu yükü daha az
  • Kullanıcı deneyimi daha akıcı

Beğeni sayılarını ise WordPress’in kendi post meta sisteminde saklayacağız. Bu sayede harici bir veritabanı tablosu açmana gerek kalmıyor.

Temel Like Sistemi Kurulumu

İlk adımda, beğeni sayısını döndüren ve güncelleyen temel fonksiyonları yazıyoruz. functions.php dosyasına aşağıdaki kodu ekle:

// functions.php - Temel like fonksiyonlari

function get_post_like_count($post_id) {
    $count = get_post_meta($post_id, '_like_count', true);
    return $count ? intval($count) : 0;
}

function update_post_like_count($post_id, $count) {
    update_post_meta($post_id, '_like_count', intval($count));
}

function user_already_liked($post_id) {
    $cookie_name = 'liked_post_' . $post_id;
    return isset($_COOKIE[$cookie_name]) && $_COOKIE[$cookie_name] == '1';
}

Bu üç fonksiyon sistemin omurgasını oluşturuyor. get_post_like_count mevcut beğeni sayısını çekiyor, update_post_like_count güncelliyor, user_already_liked ise cookie kontrolünü yapıyor.

AJAX Handler Fonksiyonu

Sayfayı yenilemeden çalışan bir like butonu için AJAX kullanman gerekiyor. WordPress’in kendi AJAX sistemini kullanacağız. Bu yaklaşım hem loggedIn hem de loggedOut kullanıcılar için çalışıyor:

// functions.php - AJAX handler

function handle_post_like() {
    // Nonce dogrulama - guvenlik kontrolu
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'post_like_nonce')) {
        wp_send_json_error(array('message' => 'Guvenlik hatasi'));
        return;
    }

    $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;

    if (!$post_id || !get_post($post_id)) {
        wp_send_json_error(array('message' => 'Gecersiz post ID'));
        return;
    }

    $cookie_name = 'liked_post_' . $post_id;
    $already_liked = isset($_COOKIE[$cookie_name]) && $_COOKIE[$cookie_name] == '1';
    $current_count = get_post_like_count($post_id);

    if ($already_liked) {
        // Unlike islemi
        $new_count = max(0, $current_count - 1);
        update_post_like_count($post_id, $new_count);
        setcookie($cookie_name, '0', time() - 3600, '/');
        $status = 'unliked';
    } else {
        // Like islemi
        $new_count = $current_count + 1;
        update_post_like_count($post_id, $new_count);
        setcookie($cookie_name, '1', time() + (365 * 24 * 60 * 60), '/');
        $status = 'liked';
    }

    wp_send_json_success(array(
        'count'  => $new_count,
        'status' => $status
    ));
}

add_action('wp_ajax_post_like', 'handle_post_like');
add_action('wp_ajax_nopriv_post_like', 'handle_post_like');

Burada dikkat edilmesi gereken birkaç nokta var. wp_ajax_nopriv_ prefix’i, giriş yapmamış kullanıcıların da bu AJAX endpoint’ini kullanabilmesini sağlıyor. Nonce doğrulaması ise spam ve CSRF saldırılarına karşı temel bir koruma katmanı sunuyor.

JavaScript ve CSS Dosyalarını Yükleme

AJAX çağrısını yapacak JavaScript kodunu ve stilini sayfaya eklemek için wp_enqueue sistemini kullanıyoruz:

// functions.php - Script ve style yukleme

function enqueue_like_button_assets() {
    if (!is_singular()) return; // Sadece tekil sayfalarda yukle

    wp_enqueue_style(
        'like-button-style',
        get_template_directory_uri() . '/css/like-button.css',
        array(),
        '1.0.0'
    );

    wp_enqueue_script(
        'like-button-script',
        get_template_directory_uri() . '/js/like-button.js',
        array('jquery'),
        '1.0.0',
        true
    );

    wp_localize_script('like-button-script', 'likeButtonData', array(
        'ajaxurl' => admin_url('admin-ajax.php'),
        'nonce'   => wp_create_nonce('post_like_nonce'),
        'post_id' => get_the_ID()
    ));
}

add_action('wp_enqueue_scripts', 'enqueue_like_button_assets');

wp_localize_script kullanımı burada çok önemli. PHP tarafındaki verileri (AJAX URL, nonce, post ID) JavaScript’e güvenli şekilde aktarmanın standart WordPress yolu bu. Asla JavaScript içinde hardcoded AJAX URL yazmayın.

JavaScript Dosyası

/js/like-button.js dosyasını oluşturup şu kodu yaz:

// js/like-button.js

jQuery(document).ready(function($) {
    $('.like-button').on('click', function(e) {
        e.preventDefault();

        var $button  = $(this);
        var $counter = $button.find('.like-count');

        // Cift tiklamayi onle
        if ($button.hasClass('loading')) return;
        $button.addClass('loading');

        $.ajax({
            url:  likeButtonData.ajaxurl,
            type: 'POST',
            data: {
                action:  'post_like',
                post_id: likeButtonData.post_id,
                nonce:   likeButtonData.nonce
            },
            success: function(response) {
                if (response.success) {
                    $counter.text(response.data.count);

                    if (response.data.status === 'liked') {
                        $button.addClass('liked');
                        $button.attr('title', 'Begeniyi kaldir');
                    } else {
                        $button.removeClass('liked');
                        $button.attr('title', 'Begen');
                    }
                }
            },
            error: function() {
                console.log('Like islemi basarisiz oldu');
            },
            complete: function() {
                $button.removeClass('loading');
            }
        });
    });
});

CSS Stillendirmesi

/css/like-button.css dosyasını oluştur:

/* css/like-button.css */

.like-button {
    display:         inline-flex;
    align-items:     center;
    gap:             6px;
    padding:         8px 16px;
    border:          2px solid #e0e0e0;
    border-radius:   25px;
    background:      #ffffff;
    cursor:          pointer;
    font-size:       14px;
    color:           #666666;
    transition:      all 0.3s ease;
    text-decoration: none;
    user-select:     none;
}

.like-button:hover {
    border-color: #ff6b6b;
    color:        #ff6b6b;
    transform:    translateY(-1px);
    box-shadow:   0 4px 12px rgba(255, 107, 107, 0.2);
}

.like-button.liked {
    background:   #ff6b6b;
    border-color: #ff6b6b;
    color:        #ffffff;
}

.like-button.loading {
    opacity: 0.6;
    cursor:  not-allowed;
}

.like-button .like-icon {
    font-size: 16px;
    line-height: 1;
}

.like-button .like-count {
    font-weight: 700;
    min-width:   20px;
    text-align:  center;
}

Like Butonunu Görüntüleme Fonksiyonu

Şimdi asıl butonu oluşturan fonksiyonu yazalım. Bu fonksiyonu hem functions.php içinden hem de template dosyalarından çağırabilirsin:

// functions.php - Like butonu HTML olusturucu

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

    $count        = get_post_like_count($post_id);
    $already_liked = user_already_liked($post_id);
    $liked_class  = $already_liked ? ' liked' : '';
    $title_text   = $already_liked ? 'Begeniyi kaldir' : 'Begen';

    $html  = '<button class="like-button' . $liked_class . '" ';
    $html .= 'data-post-id="' . esc_attr($post_id) . '" ';
    $html .= 'title="' . esc_attr($title_text) . '" ';
    $html .= 'aria-label="' . esc_attr($title_text) . '">';
    $html .= '<span class="like-icon">&#10084;</span>';
    $html .= '<span class="like-count">' . esc_html($count) . '</span>';
    $html .= '</button>';

    return $html;
}

function display_like_button($post_id = null) {
    echo get_like_button($post_id);
}

Template dosyalarında kullanımı çok basit. Mesela single.php veya temanın içerik dosyasına şunu ekleyebilirsin:

<!-- single.php veya content.php icinde -->
<?php
if (function_exists('display_like_button')) {
    display_like_button();
}
?>

İçeriğe Otomatik Ekleme (Filter ile)

Eğer her yazının altına otomatik olarak like butonu eklemek istiyorsan, template dosyalarına dokunmana gerek yok. the_content filtresi ile bunu yapabilirsin:

// functions.php - Icerige otomatik ekleme

function auto_append_like_button($content) {
    if (!is_singular('post')) return $content;
    if (!in_the_loop())       return $content;
    if (!is_main_query())     return $content;

    $like_button = '<div class="post-like-wrapper">';
    $like_button .= '<p class="like-label">Bu yaziyi begendiysen:</p>';
    $like_button .= get_like_button(get_the_ID());
    $like_button .= '</div>';

    return $content . $like_button;
}

add_filter('the_content', 'auto_append_like_button');

Bu yöntem özellikle sayfa sayısı fazla olan sitelerde çok kullanışlı. Her template dosyasına tek tek ekleme yapmak zorunda kalmıyorsun.

Beğeni Sayısına Göre Sıralama

En çok beğenilen yazıları listelemek istiyorsan, WP_Query ile bu veriyi kullanabilirsin:

// En cok begenilen 5 yaziyi getir

function get_most_liked_posts($count = 5) {
    $args = array(
        'post_type'      => 'post',
        'post_status'    => 'publish',
        'posts_per_page' => $count,
        'meta_key'       => '_like_count',
        'orderby'        => 'meta_value_num',
        'order'          => 'DESC',
        'meta_query'     => array(
            array(
                'key'     => '_like_count',
                'value'   => 0,
                'compare' => '>',
                'type'    => 'NUMERIC'
            )
        )
    );

    return new WP_Query($args);
}

// Widget veya shortcode icinde kullanim ornegi
function most_liked_posts_shortcode($atts) {
    $atts  = shortcode_atts(array('count' => 5), $atts);
    $query = get_most_liked_posts(intval($atts['count']));

    if (!$query->have_posts()) {
        return '<p>Henuz begenilen yazi yok.</p>';
    }

    $output = '<ul class="most-liked-posts">';
    while ($query->have_posts()) {
        $query->the_post();
        $count   = get_post_like_count(get_the_ID());
        $output .= '<li>';
        $output .= '<a href="' . get_permalink() . '">' . get_the_title() . '</a>';
        $output .= ' <span class="like-badge">' . $count . ' beğeni</span>';
        $output .= '</li>';
    }
    $output .= '</ul>';
    wp_reset_postdata();

    return $output;
}

add_shortcode('most_liked_posts', 'most_liked_posts_shortcode');

Bu shortcode’u herhangi bir sayfada [most_liked_posts count="10"] şeklinde kullanabilirsin.

Güvenlik Geliştirmeleri ve Rate Limiting

Temel sistemde bir kullanıcı her sayfa yüklemesinde nonce alıyor. Ama kötü niyetli biri script ile sürekli istek gönderebilir. Buna karşı basit bir rate limiting ekleyebilirsin:

// functions.php - Rate limiting eklenmis like handler

function handle_post_like_with_rate_limit() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'post_like_nonce')) {
        wp_send_json_error(array('message' => 'Guvenlik hatasi'));
        return;
    }

    // Rate limiting: Ayni IP'den 60 saniyede max 30 istek
    $ip_key    = 'like_rate_' . md5($_SERVER['REMOTE_ADDR']);
    $hit_count = get_transient($ip_key);

    if ($hit_count === false) {
        set_transient($ip_key, 1, 60);
    } elseif ($hit_count >= 30) {
        wp_send_json_error(array('message' => 'Cok fazla istek'));
        return;
    } else {
        set_transient($ip_key, $hit_count + 1, 60);
    }

    $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;

    if (!$post_id || !get_post($post_id)) {
        wp_send_json_error(array('message' => 'Gecersiz istek'));
        return;
    }

    $cookie_name   = 'liked_post_' . $post_id;
    $already_liked = isset($_COOKIE[$cookie_name]) && $_COOKIE[$cookie_name] == '1';
    $current_count = get_post_like_count($post_id);

    if ($already_liked) {
        $new_count = max(0, $current_count - 1);
        update_post_like_count($post_id, $new_count);
        setcookie($cookie_name, '0', time() - 3600, '/');
        $status = 'unliked';
    } else {
        $new_count = $current_count + 1;
        update_post_like_count($post_id, $new_count);
        setcookie($cookie_name, '1', time() + (365 * 24 * 60 * 60), '/');
        $status = 'liked';
    }

    wp_send_json_success(array(
        'count'  => $new_count,
        'status' => $status
    ));
}

WordPress Transients API’yi burada hem rate limiting hem de geçici cache için kullanabilirsin. Transient’lar otomatik olarak süresi dolduğunda temizleniyor, bu yüzden ekstra bakım gerektirmiyor.

Gerçek Dünya Senaryoları

Senaryo 1: Haber sitesi Bir haber sitesi işletiyorsun ve editörler hangi haberlerin daha ilgi gördüğünü görmek istiyor. _like_count değerini WordPress admin panelindeki post listesinde göstermek için manage_posts_columns filter’ını kullanabilirsin. Böylece editörler yazılar sayfasında beğeni sütununu görebilir.

Senaryo 2: Tarif blogu Tarif blogu yapıyorsun ve en popüler tarifleri sidebar’da listelemek istiyorsun. Yukarıdaki most_liked_posts_shortcode fonksiyonunu bir widget’a dönüştürüp sidebar’a ekleyebilirsin. [most_liked_posts count="5"] shortcode’unu da text widget’a yazarak hızlıca kullanabilirsin.

Senaryo 3: Çoklu post tipi desteği Sadece yazılar değil, custom post type’lar için de like butonu çalıştırmak istiyorsan auto_append_like_button fonksiyonundaki is_singular('post') kontrolünü is_singular(array('post', 'recipe', 'product')) şeklinde genişletebilirsin.

Admin Panelinde Beğeni Sayısını Gösterme

Yönetici olarak hangi yazının kaç beğeni aldığını görmek pratik bir ihtiyaç. Bunun için post listesine bir sütun ekleyebilirsin:

// functions.php - Admin paneli entegrasyonu

function add_like_count_column($columns) {
    $columns['like_count'] = 'Beğeni';
    return $columns;
}
add_filter('manage_posts_columns', 'add_like_count_column');

function show_like_count_column($column, $post_id) {
    if ($column === 'like_count') {
        $count = get_post_like_count($post_id);
        echo '<strong>' . esc_html($count) . '</strong>';
    }
}
add_action('manage_posts_custom_column', 'show_like_count_column', 10, 2);

function make_like_count_sortable($columns) {
    $columns['like_count'] = 'like_count';
    return $columns;
}
add_filter('manage_edit-posts_sortable_columns', 'make_like_count_sortable');

function like_count_orderby($query) {
    if (!is_admin()) return;
    $orderby = $query->get('orderby');
    if ($orderby === 'like_count') {
        $query->set('meta_key', '_like_count');
        $query->set('orderby', 'meta_value_num');
    }
}
add_action('pre_get_posts', 'like_count_orderby');

Bu kod parçası sayesinde WordPress admin panelindeki yazılar listesinde “Beğeni” adlı bir sütun görürsün. Üstelik bu sütuna tıklayarak sıralama da yapabilirsin, en çok beğenilen yazıyı anında görebilirsin.

Sık Karşılaşılan Sorunlar

AJAX çalışmıyor: En yaygın sorun ajaxurl değişkeninin tanımlı olmaması. wp_localize_script ile scriptin doğru sıraya yüklendiğinden emin ol. Script handle’ının wp_enqueue_script ile kaydettiğin isimle birebir aynı olması lazım.

Cookie çalışmıyor: Eğer site HTTPS kullanıyorsa ve cookie setcookie ile set ediliyorsa, 6. parametre olarak güvenli bayrak eklemek gerekebilir. setcookie($cookie_name, '1', time() + 31536000, '/', '', is_ssl(), true) şeklinde güncelleyebilirsin.

Beğeni sayısı sıfırlanıyor: get_post_meta false döndürdüğünde intval(false) sıfır veriyor. Bu normal davranış. Ama update_post_meta çağrısı öncesinde post_id doğrulaması yapmazsan yanlış post için meta kaydedebilirsin. Her zaman intval() ile tip güvenliğini sağla.

Cache problemi: Sayfa cache kullanan sitelerde (W3 Total Cache, WP Rocket) like butonu ilk renderda cookie durumunu yanlış gösterebilir. Bunun için buton durumunu JavaScript tarafında cookie okuyarak ayarlamayı tercih edebilirsin. Sayfa load’unda document.cookie içinde ilgili cookie var mı kontrol et ve class’ı dinamik olarak ekle.

Performans İpuçları

  • _like_count meta değerini çok sık sorgulayacaksan, sonucu bir değişkende cache’le. Aynı sayfa içinde aynı post için birden fazla kez get_post_meta çağrısı yapmak gereksiz sorgu demek.
  • is_singular() kontrolünü her zaman wp_enqueue_scripts içinde yap. Script’i tüm sayfalara yüklemek sayfa hızını gereksiz yere etkiler.
  • Çok yoğun trafik alıyorsan rate limiting için Transient yerine object cache (Redis/Memcached) destekli bir çözüm değerlendir.

Sonuç

Bu yazıda sıfırdan, eklentisiz ve üyelik gerektirmeyen bir WordPress like butonu sistemi kurduk. Cookie tabanlı kontrol, WordPress AJAX sistemi, nonce güvenliği ve admin panel entegrasyonu ile production’a hazır bir yapı elde etmiş olduk.

Sistemin tüm parçaları birbirinden bağımsız çalışıyor. Sadece temel like fonksiyonunu kullanmak istersen, AJAX kısmını atlayıp form submit ile de yapabilirsin. Sadece admin sütununu istiyorsan sadece o kısmı ekleyebilirsin.

En önemli nokta: her zaman nonce doğrulaması yap. Güvensiz AJAX endpoint’leri, kötü niyetli kişilerin beğeni sayılarını manipüle etmesine açık kapı bırakır. wp_verify_nonce tek satır ama kritik bir savunma katmanı.

Bu altyapıyı kurduktan sonra genişletmek çok kolay. Beğenilere e-posta bildirimi ekleyebilir, toplam beğeni sayısına göre rozet sistemi kurabilir veya WooCommerce entegrasyonuyla en beğenilen ürünleri öne çıkarabilirsin. Temel yapı sağlam, gerisini ihtiyacına göre şekillendirebilirsin.

Bir yanıt yazın

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