WooCommerce Ürün Kartı Başlığının Altına Rozet Ekleme

WooCommerce mağazanızda ürün kartlarına rozet eklemek, hem kullanıcı deneyimini geliştiren hem de satışları doğrudan etkileyen küçük ama güçlü bir detaydır. “Yeni”, “Çok Satan”, “Sınırlı Stok” gibi rozetler müşterinin dikkatini çeker ve satın alma kararını hızlandırır. Bu yazıda, functions.php dosyasına ekleyeceğiniz kod parçacıklarıyla ürün başlığının altına nasıl dinamik rozetler ekleyebileceğinizi adım adım inceleyeceğiz.

Neden Ürün Başlığının Altına Rozet?

WooCommerce’in varsayılan rozet sistemi yalnızca “Sale” (indirimli) etiketini destekler ve bu rozet genellikle ürün görselinin üzerine gelir. Oysa başlığın hemen altına yerleştirilen bir rozet, kullanıcı ürün adını okurken göz önüne girer. Bu konum:

  • Görsel kalabalığından bağımsız olarak her zaman okunabilir kalır
  • Mobil cihazlarda görselin üstündeki rozetlere göre çok daha net görünür
  • Birden fazla rozeti yan yana sıralamaya olanak tanır
  • CSS ile kolayca özelleştirilebilir

Bir müşterinin “Organik Zeytinyağı 500ml” başlığını okurken hemen altında 🔥 Çok Satan yazdığını görmesi, ürünü sepete atma olasılığını ciddi ölçüde artırır.

Temel Hook: woocommerce_after_shop_loop_item_title

WooCommerce, ürün kartlarında çeşitli hook noktaları sunar. Başlığın hemen altına içerik eklemek için kullanacağımız hook woocommerce_after_shop_loop_item_title‘dır. Bu hook, ürün döngüsünde (shop, kategori, arama sonuçları) başlık render edildikten hemen sonra tetiklenir.

// Temel yapı - functions.php'ye eklenir
add_action( 'woocommerce_after_shop_loop_item_title', 'custom_product_badge', 5 );

function custom_product_badge() {
    global $product;
    echo '<span class="custom-badge">Yeni</span>';
}

Hook öncelik değeri olan 5, WooCommerce’in kendi fiyat hook’undan (öncelik 10) önce çalışmasını sağlar. Böylece rozetiniz fiyatın üstünde, başlığın hemen altında görünür.

Senaryo 1: “Yeni Ürün” Rozeti

Ürünün yayınlanma tarihine göre otomatik rozet ekleyelim. Örneğin son 30 gün içinde eklenen ürünlere “Yeni” rozeti gösterelim.

add_action( 'woocommerce_after_shop_loop_item_title', 'show_new_product_badge', 5 );

function show_new_product_badge() {
    global $product;

    $post_date    = get_the_date( 'U' ); // Unix timestamp
    $current_date = current_time( 'timestamp' );
    $days_old     = ( $current_date - $post_date ) / DAY_IN_SECONDS;

    if ( $days_old <= 30 ) {
        echo '<span class="product-badge badge-new">🆕 Yeni</span>';
    }
}

Bu yaklaşımın güzelliği tamamen otomatik olmasıdır. Ürünü eklediniz, 30 gün boyunca rozet görünür, sonra kaybolur. Elle müdahaleye gerek yoktur.

Gün sayısını dinamik tutmak isterseniz bir seçenek değeri ile yönetebilirsiniz. WordPress ayarlarından ya da doğrudan koda sabit değer olarak tanımlayabilirsiniz. Küçük mağazalar için 30 gün uygundur, büyük mağazalar için 14 gün daha gerçekçi bir “yeni” sınırı oluşturur.

Senaryo 2: İndirim Yüzdesi Rozeti

WooCommerce’in varsayılan “Sale” rozeti yüzde göstermez. Müşteri “%40 indirim” yazısını görünce çok daha motive olur.

add_action( 'woocommerce_after_shop_loop_item_title', 'show_discount_percentage_badge', 5 );

function show_discount_percentage_badge() {
    global $product;

    if ( ! $product->is_on_sale() ) {
        return;
    }

    // Basit ürün veya varyasyonlu ürün kontrolü
    if ( $product->is_type( 'simple' ) ) {
        $regular_price = (float) $product->get_regular_price();
        $sale_price    = (float) $product->get_sale_price();
    } elseif ( $product->is_type( 'variable' ) ) {
        $regular_price = (float) $product->get_variation_regular_price( 'max' );
        $sale_price    = (float) $product->get_variation_sale_price( 'min' );
    } else {
        return;
    }

    if ( $regular_price > 0 ) {
        $discount = round( ( ( $regular_price - $sale_price ) / $regular_price ) * 100 );
        echo '<span class="product-badge badge-sale">-%' . $discount . ' İndirim</span>';
    }
}

Bu kod varyasyonlu ürünlerde en düşük indirimli fiyat ile en yüksek normal fiyatı karşılaştırarak maksimum indirim yüzdesini gösterir. Bazı mağazalar minimum indirimi göstermeyi tercih eder, “en az %X indirim” şeklinde. Hangi stratejiyi seçeceğiniz tamamen pazarlama kararınıza bağlıdır.

Senaryo 3: Stok Durumuna Göre Rozet

Sınırlı stok uyarısı, aciliyet hissi (FOMO) yaratır. WooCommerce’in stok miktarı özelliğini kullanarak dinamik bir uyarı rozeti ekleyebiliriz.

add_action( 'woocommerce_after_shop_loop_item_title', 'show_stock_badge', 5 );

function show_stock_badge() {
    global $product;

    // Stok yönetimi aktif mi?
    if ( ! $product->managing_stock() ) {
        return;
    }

    $stock_qty = $product->get_stock_quantity();

    if ( $stock_qty === null ) {
        return;
    }

    if ( $stock_qty <= 0 ) {
        echo '<span class="product-badge badge-outofstock">❌ Tükendi</span>';
    } elseif ( $stock_qty <= 5 ) {
        echo '<span class="product-badge badge-lowstock">⚠️ Son ' . $stock_qty . ' Ürün!</span>';
    } elseif ( $stock_qty <= 20 ) {
        echo '<span class="product-badge badge-limitedstock">Sınırlı Stok</span>';
    }
}

Stok sayısını doğrudan göstermek konusunda dikkatli olun. “Son 3 ürün” yazısı aciliyet yaratır ama stok bilgisini rakiplerinizle de paylaşmış olursunuz. Rakip yoğunluğu düşük bir niş mağazaysanız bu sorun değil, ama rekabetçi bir sektördeyseniz sadece “Sınırlı Stok” yazmak daha akıllıca olabilir.

Senaryo 4: Özel Meta Alana Göre Rozet

Bazı durumlarda otomatik kriterler yetmez, ürün bazında elle rozet atamak istersiniz. Bunun için bir custom field (özel meta alan) kullanabiliriz. ACF (Advanced Custom Fields) veya WooCommerce’in kendi meta alanları bu iş için uygundur.

add_action( 'woocommerce_after_shop_loop_item_title', 'show_custom_meta_badge', 5 );

function show_custom_meta_badge() {
    global $product;
    $product_id = $product->get_id();

    // '_product_badge' adlı özel meta alanını oku
    $badge_text = get_post_meta( $product_id, '_product_badge', true );

    if ( empty( $badge_text ) ) {
        return;
    }

    // Güvenlik: HTML encode
    $badge_text = esc_html( $badge_text );

    // Önceden tanımlı rozet renklerini eşleştir
    $badge_colors = array(
        'Öne Çıkan'    => 'badge-featured',
        'Organik'       => 'badge-organic',
        'Çok Satan'     => 'badge-bestseller',
        'Kampanya'      => 'badge-campaign',
        'Ücretsiz Kargo'=> 'badge-freeship',
    );

    $badge_class = isset( $badge_colors[ $badge_text ] )
        ? $badge_colors[ $badge_text ]
        : 'badge-default';

    echo '<span class="product-badge ' . $badge_class . '">' . $badge_text . '</span>';
}

Ürün düzenleme ekranında “_product_badge” meta alanına “Organik” yazdığınızda o ürünün kartında yeşil renkli organik rozeti görünecektir. ACF kullanıyorsanız alan adını ACF’in belirlediği isimle eşleştirmeyi unutmayın.

Senaryo 5: Birden Fazla Rozeti Birleştirme

Gerçek dünya senaryolarında bir ürün aynı anda hem yeni hem de indirimli olabilir. Tüm rozetleri tek bir fonksiyona toplayıp bir container div içinde render etmek en temiz yaklaşımdır.

add_action( 'woocommerce_after_shop_loop_item_title', 'show_all_product_badges', 5 );

function show_all_product_badges() {
    global $product;

    $badges     = array();
    $product_id = $product->get_id();

    // Yeni ürün kontrolü
    $post_date  = get_the_date( 'U' );
    $days_old   = ( current_time( 'timestamp' ) - $post_date ) / DAY_IN_SECONDS;
    if ( $days_old <= 30 ) {
        $badges[] = '<span class="product-badge badge-new">Yeni</span>';
    }

    // İndirim kontrolü
    if ( $product->is_on_sale() && $product->is_type( 'simple' ) ) {
        $regular = (float) $product->get_regular_price();
        $sale    = (float) $product->get_sale_price();
        if ( $regular > 0 ) {
            $pct      = round( ( ( $regular - $sale ) / $regular ) * 100 );
            $badges[] = '<span class="product-badge badge-sale">-%' . $pct . '</span>';
        }
    }

    // Stok uyarısı
    if ( $product->managing_stock() ) {
        $qty = $product->get_stock_quantity();
        if ( $qty !== null && $qty > 0 && $qty <= 5 ) {
            $badges[] = '<span class="product-badge badge-lowstock">Son ' . $qty . ' ürün</span>';
        }
    }

    // Özel meta rozeti
    $custom = get_post_meta( $product_id, '_product_badge', true );
    if ( ! empty( $custom ) ) {
        $badges[] = '<span class="product-badge badge-custom">' . esc_html( $custom ) . '</span>';
    }

    // Boşsa hiçbir şey çizme
    if ( empty( $badges ) ) {
        return;
    }

    echo '<div class="product-badges-wrapper">';
    echo implode( ' ', $badges );
    echo '</div>';
}

Bu yapı ölçeklenebilir. Yeni bir rozet türü eklemek istediğinizde sadece $badges[] dizisine yeni bir satır ekliyorsunuz, mevcut koda dokunmuyorsunuz.

CSS: Rozetleri Güzel Göstermek

Kod çalışıyor olsa bile rozetler CSS olmadan ham metin gibi görünür. Temanızın style.css dosyasına ya da WordPress Özelleştirici’nin “Ek CSS” bölümüne aşağıdaki stilleri ekleyin.

/* functions.php içindeki wp_head hook'una veya CSS dosyanıza ekleyin */
add_action( 'wp_head', 'product_badges_inline_css' );

function product_badges_inline_css() {
    echo '<style>
    .product-badges-wrapper {
        display: flex;
        flex-wrap: wrap;
        gap: 5px;
        margin: 5px 0 8px 0;
    }
    .product-badge {
        display: inline-block;
        padding: 2px 8px;
        border-radius: 3px;
        font-size: 11px;
        font-weight: 700;
        line-height: 1.6;
        text-transform: uppercase;
        letter-spacing: 0.5px;
    }
    .badge-new       { background: #2196F3; color: #fff; }
    .badge-sale      { background: #F44336; color: #fff; }
    .badge-lowstock  { background: #FF9800; color: #fff; }
    .badge-bestseller{ background: #9C27B0; color: #fff; }
    .badge-organic   { background: #4CAF50; color: #fff; }
    .badge-featured  { background: #FF5722; color: #fff; }
    .badge-freeship  { background: #009688; color: #fff; }
    .badge-default   { background: #607D8B; color: #fff; }
    .badge-custom    { background: #795548; color: #fff; }
    .badge-outofstock{ background: #9E9E9E; color: #fff; }
    </style>';
}

CSS’i wp_head ile inline eklemek küçük mağazalar için pratiktir. Büyük mağazalarda bu stilleri temanızın CSS dosyasına taşıyıp wp_enqueue_style ile kayıt ettirmeniz daha performanslı olacaktır.

Performans Optimizasyonu: Transient Cache

Stok sorgusu ve meta okumaları döngü içinde çalışır. Binlerce ürünlü bir mağazada her sayfa yüklemesinde onlarca veritabanı sorgusu çalışabilir. Bu yükü azaltmak için WordPress transient sistemini kullanabilirsiniz.

function get_product_badges_cached( $product_id ) {
    $cache_key  = 'product_badges_' . $product_id;
    $cached     = get_transient( $cache_key );

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

    $product = wc_get_product( $product_id );
    $badges  = array();

    // Yeni ürün
    $post_date = get_the_date( 'U', $product_id );
    $days_old  = ( current_time( 'timestamp' ) - $post_date ) / DAY_IN_SECONDS;
    if ( $days_old <= 30 ) {
        $badges[] = '<span class="product-badge badge-new">Yeni</span>';
    }

    // İndirim (stok gibi dinamik veriler için cache süresi kısa tutulur)
    if ( $product->is_on_sale() ) {
        $badges[] = '<span class="product-badge badge-sale">İndirimli</span>';
    }

    // Cache: indirim ve stok gibi değişen veriler için 1 saat yeterli
    set_transient( $cache_key, $badges, HOUR_IN_SECONDS );

    return $badges;
}

add_action( 'woocommerce_after_shop_loop_item_title', 'show_cached_badges', 5 );

function show_cached_badges() {
    global $product;
    $badges = get_product_badges_cached( $product->get_id() );

    if ( empty( $badges ) ) {
        return;
    }

    echo '<div class="product-badges-wrapper">';
    echo implode( ' ', $badges );
    echo '</div>';
}

Dikkat: Stok miktarı gibi anlık değişen veriler için cache kullanırken süreyi kısa tutun. Bir müşteri “Son 3 ürün” yazısını görüp ürün sayfasına gittiğinde stok 0 olmuşsa bu kötü bir deneyimdir. Kritik stok bilgisi için cacheyi ya kullanmayın ya da 10-15 dakika gibi çok kısa bir süre belirleyin.

Rozet Görünürlüğünü Ürün Sayfasına da Taşımak

Shop sayfasında rozet görünüyor ama müşteri ürün sayfasına geçtiğinde rozet kayboluyor. Tutarlı bir deneyim için aynı rozeti ürün detay sayfasına da eklemek mantıklıdır.

// Ürün detay sayfası için ayrı hook
add_action( 'woocommerce_single_product_summary', 'show_badges_on_product_page', 6 );

function show_badges_on_product_page() {
    // Yukarıda tanımladığımız fonksiyonu yeniden kullanıyoruz
    show_all_product_badges();
}

Hook önceliği 6 olarak ayarlandı çünkü woocommerce_single_product_summary‘de öncelik 5 ile başlık, öncelik 10 ile fiyat render edilir. Rozetlerin başlık ile fiyat arasında görünmesini sağlamak için 6 ideal bir değerdir.

Admin Panelinde Rozet Yönetimi

Ürün bazında rozet atamayı kolaylaştırmak için WooCommerce ürün düzenleme ekranına basit bir meta kutusu ekleyebilirsiniz.

// Admin: Ürün meta kutusu ekle
add_action( 'add_meta_boxes', 'add_product_badge_metabox' );

function add_product_badge_metabox() {
    add_meta_box(
        'product_badge_box',
        'Ürün Rozeti',
        'render_product_badge_metabox',
        'product',
        'side',
        'high'
    );
}

function render_product_badge_metabox( $post ) {
    $badge = get_post_meta( $post->ID, '_product_badge', true );
    $options = array(
        ''               => 'Rozet Yok',
        'Öne Çıkan'     => 'Öne Çıkan',
        'Organik'        => 'Organik',
        'Çok Satan'      => 'Çok Satan',
        'Kampanya'       => 'Kampanya',
        'Ücretsiz Kargo' => 'Ücretsiz Kargo',
    );

    wp_nonce_field( 'save_product_badge', 'product_badge_nonce' );

    echo '<select name="product_badge" style="width:100%">';
    foreach ( $options as $val => $label ) {
        $selected = selected( $badge, $val, false );
        echo '<option value="' . esc_attr( $val ) . '" ' . $selected . '>' . esc_html( $label ) . '</option>';
    }
    echo '</select>';
}

// Kaydetme işlemi
add_action( 'save_post_product', 'save_product_badge_meta' );

function save_product_badge_meta( $post_id ) {
    if ( ! isset( $_POST['product_badge_nonce'] ) ) {
        return;
    }
    if ( ! wp_verify_nonce( $_POST['product_badge_nonce'], 'save_product_badge' ) ) {
        return;
    }
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    $badge = isset( $_POST['product_badge'] ) ? sanitize_text_field( $_POST['product_badge'] ) : '';
    update_post_meta( $post_id, '_product_badge', $badge );
}

Bu kod admin panelinde ürün düzenleme sayfasının sağ sütununa “Ürün Rozeti” adında bir kutu ekler. İçerik editörleri teknik bilgiye gerek kalmadan açılır menüden rozet seçebilir.

Yaygın Sorunlar ve Çözümleri

Rozet görünmüyor: Hook adını kontrol edin. Bazı temalar WooCommerce şablonlarını override eder ve standart hook’ları kaldırır. Temanızın woocommerce/content-product.php dosyasını inceleyin. Hook yoksa şablona manuel olarak do_action( 'woocommerce_after_shop_loop_item_title' ) ekleyin.

Rozet yanlış yerde çıkıyor: Öncelik değerini ayarlayın. 5 yerine 1 yaparsanız daha yukarı çıkar, 9 yaparsanız fiyata yaklaşır.

Varyasyonlu ürünlerde indirim yanlış hesaplanıyor: get_variation_regular_price ve get_variation_sale_price metodlarının min ve max parametrelerini doğru kullandığınızdan emin olun.

PHP hatası: $product bulunamadı: global $product; satırını fonksiyon içinde en üste eklemeyi unutmayın. Döngü dışında kullanıyorsanız wc_get_product( get_the_ID() ) ile alın.

Mobil’de rozetler üst üste biniyor: CSS’te flex-wrap: wrap ve gap değerlerini artırın. Küçük ekranlar için media query ile font-size’ı küçültün.

Sonuç

WooCommerce ürün kartlarına rozet eklemek, bir-iki hook ve birkaç satır CSS ile hayata geçirebileceğiniz, etkisi yüksek bir özelleştirmedir. Bu yazıda ele aldığımız yaklaşımları özetleyecek olursak:

  • woocommerce_after_shop_loop_item_title hook’u, başlık altı için en uygun noktadır
  • Tarih bazlı “Yeni”, indirim yüzdeli “Sale”, stok bazlı “Sınırlı” rozetleri tamamen otomatik çalışır
  • Özel meta alanı ile içerik ekiplerine el ile rozet atama yetkisi verebilirsiniz
  • Tüm rozetleri tek bir container içinde birleştirmek tasarım tutarlılığı sağlar
  • Yoğun kataloglarda transient cache ile sorgu yükünü azaltın
  • Aynı rozet mantığını woocommerce_single_product_summary hook’u ile ürün detay sayfasına da taşıyın

Küçük bir mağazayla başlıyorsanız sadece “yeni ürün” ve “indirim yüzdesi” rozetleriyle başlayın. İhtiyaçlarınız büyüdükçe stok uyarısı ve özel meta rozetlerini ekleyin. Her eklemeden önce ve sonra dönüşüm oranlarınızı takip edin; hangi rozetin satışa gerçek katkı sağladığını verilerle görürsünüz.

Bir yanıt yazın

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