WooCommerce Ürün Listesinde Her Ürünün Altına İçerik Ekleme

E-ticaret sitelerinde ürün listesi sayfaları, ziyaretçilerin en çok zaman geçirdiği yerlerden biridir. WooCommerce’in varsayılan ürün listeleme görünümü oldukça sade kalır ve çoğu zaman ihtiyacınıza yetmez. Bir ürünün altına “Ücretsiz Kargo” etiketi, stok durumu, özel bir promosyon mesajı veya hatta bir “Hızlı Satın Al” butonu eklemek istediğinizde, theme dosyalarını karıştırmak yerine functions.php üzerinden çok daha temiz ve sürdürülebilir bir çözüm üretebilirsiniz. Bu yazıda, WooCommerce ürün listesinde her ürünün altına içerik eklemenin birden fazla yolunu, gerçek dünya senaryolarıyla birlikte ele alacağız.

WooCommerce Hook Sistemi ve Ürün Listesi Anatomisi

WooCommerce, WordPress’in hook sistemini çok yoğun biçimde kullanır. Ürün listeleme sayfalarında (kategori sayfaları, shop sayfası, arama sonuçları) her ürün kartı render edilirken onlarca action hook tetiklenir. Bu hook’lara işlev bağlamak, theme dosyalarına dokunmadan istediğiniz içeriği enjekte etmenizi sağlar.

Ürün listesinde en çok kullanılan hook’lar şunlardır:

  • woocommerce_after_shop_loop_item: Ürün linkinin kapandıktan hemen sonra tetiklenir, ürün kartının en altında çalışır
  • woocommerce_after_shop_loop_item_title: Ürün başlığından sonra, fiyattan önce çalışır
  • woocommerce_before_shop_loop_item: Ürün linkinin açılmadan önce tetiklenir
  • woocommerce_shop_loop_item_title: Ürün başlığının kendisinin render edildiği hook
  • woocommerce_before_shop_loop_item_title: Ürün görselinden hemen sonra, başlıktan önce çalışır

Her hook’un bir de priority parametresi vardır. WooCommerce varsayılan olarak bazı işlevleri belirli öncelik değerleriyle bu hook’lara bağlar. Örneğin woocommerce_after_shop_loop_item_title hook’una fiyat 10 önceliğiyle, yıldız derecelendirmesi ise 5 önceliğiyle bağlıdır. Bunu bilmek, içeriğinizi tam istediğiniz yere yerleştirmenizi sağlar.

Temel Kullanım: Her Ürünün Altına Basit Mesaj Ekleme

En temel örnek ile başlayalım. Shop sayfasında her ürün kartının altına sabit bir metin ekleyelim:

// functions.php'ye ekleyin
add_action( 'woocommerce_after_shop_loop_item', 'custom_after_product_item', 15 );

function custom_after_product_item() {
    echo '<div class="custom-product-badge">';
    echo '<span>✓ Ücretsiz Kargo ve Hızlı Teslimat</span>';
    echo '</div>';
}

Burada 15 öncelik değeri kullandık, çünkü WooCommerce’in kendi “Sepete Ekle” butonu bu hook’a 10 önceliğiyle bağlıdır. 15 kullanarak butunun altına yerleştirilmesini sağladık. Eğer butonun üzerine çıkmak isteseydiniz 9 veya daha düşük bir değer kullanmanız gerekirdi.

Dinamik İçerik: Ürün Verilerine Göre Koşullu Gösterim

Gerçek senaryolarda statik metin yeterli olmaz. Ürünün özelliklerine göre farklı içerik göstermek isteyebilirsiniz. Aşağıdaki örnek, stok durumuna göre farklı mesajlar gösterir:

add_action( 'woocommerce_after_shop_loop_item', 'custom_stock_message', 15 );

function custom_stock_message() {
    global $product;

    if ( ! $product ) {
        return;
    }

    $stock_qty = $product->get_stock_quantity();
    $stock_status = $product->get_stock_status();

    if ( $stock_status === 'outofstock' ) {
        echo '<p class="stock-warning" style="color:#e74c3c; font-size:13px; margin:5px 0;">
              ✗ Stokta Yok - Listeye Ekle</p>';
    } elseif ( $stock_qty !== null && $stock_qty <= 5 && $stock_qty > 0 ) {
        echo '<p class="stock-warning" style="color:#e67e22; font-size:13px; margin:5px 0;">
              ⚡ Son ' . intval( $stock_qty ) . ' ürün kaldı!</p>';
    } elseif ( $stock_status === 'instock' ) {
        echo '<p class="stock-ok" style="color:#27ae60; font-size:13px; margin:5px 0;">
              ✓ Stokta Mevcut</p>';
    }
}

Bu kod, ürün stok yönetimi aktif olan ürünlerde gerçek stok adedine bakarak kullanıcıyı bilgilendirir. $product global değişkeni WooCommerce döngüsü içinde her iterasyonda otomatik olarak güncellenir, dolayısıyla her ürün için doğru veriyi alırsınız.

Özel Ürün Meta Verisine Göre İçerik Ekleme

Ürünlerinize özel meta veri ekliyorsanız (örneğin bir “öne çıkan özellik” alanı veya “garanti süresi” gibi custom field’lar), bu verileri de ürün listesinde gösterebilirsiniz:

add_action( 'woocommerce_after_shop_loop_item_title', 'custom_product_meta_display', 8 );

function custom_product_meta_display() {
    global $product;

    if ( ! $product ) {
        return;
    }

    $product_id = $product->get_id();

    // ACF veya herhangi bir custom field
    $guarantee = get_post_meta( $product_id, '_guarantee_period', true );
    $badge_text = get_post_meta( $product_id, '_custom_badge', true );

    if ( ! empty( $badge_text ) ) {
        echo '<span class="custom-product-badge" style="
              display:inline-block;
              background:#3498db;
              color:#fff;
              padding:3px 10px;
              border-radius:3px;
              font-size:12px;
              margin-bottom:8px;">'
              . esc_html( $badge_text ) . '</span><br>';
    }

    if ( ! empty( $guarantee ) ) {
        echo '<span class="guarantee-info" style="font-size:12px; color:#666;">
              🛡 ' . esc_html( $guarantee ) . ' Garanti</span>';
    }
}

Burada öncelik değeri olarak 8 kullandık. Hatırlarsanız yıldız derecelendirmesi 5, fiyat ise 10 önceliğiyle aynı hook’a bağlıdır. 8 değeri ile içeriğimiz fiyattan önce, yıldızlardan sonra görünecektir.

Ürün Kategorisine Göre Farklı İçerik Gösterme

Belirli kategorilerdeki ürünlere özel içerik eklemek, özellikle büyük e-ticaret sitelerinde çok işe yarar. Örneğin “elektronik” kategorisindeki ürünlere teknik destek bilgisi, “giyim” kategorisindekine iade politikası ekleyebilirsiniz:

add_action( 'woocommerce_after_shop_loop_item', 'category_specific_content', 12 );

function category_specific_content() {
    global $product;

    if ( ! $product ) {
        return;
    }

    $product_id = $product->get_id();
    $categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'slugs' ) );

    if ( is_wp_error( $categories ) || empty( $categories ) ) {
        return;
    }

    $messages = array(
        'elektronik'    => array(
            'icon'  => '🔧',
            'text'  => '2 Yıl Teknik Destek',
            'color' => '#2980b9',
        ),
        'giyim'         => array(
            'icon'  => '↩',
            'text'  => '30 Gün Ücretsiz İade',
            'color' => '#8e44ad',
        ),
        'mutfak-araclari' => array(
            'icon'  => '📦',
            'text'  => 'Aynı Gün Kargo',
            'color' => '#16a085',
        ),
    );

    foreach ( $categories as $cat_slug ) {
        if ( isset( $messages[ $cat_slug ] ) ) {
            $msg = $messages[ $cat_slug ];
            echo '<div class="cat-specific-msg" style="
                  font-size:12px;
                  color:' . esc_attr( $msg['color'] ) . ';
                  margin-top:6px;
                  padding:4px 0;">'
                  . esc_html( $msg['icon'] ) . ' '
                  . esc_html( $msg['text'] )
                  . '</div>';
            break; // İlk eşleşmede dur
        }
    }
}

Bu yapı ile $messages dizisine yeni kategori-mesaj çiftleri ekleyerek kolayca genişletebilirsiniz.

Fiyat Karşılaştırma ve İndirim Rozeti

WooCommerce zaten indirimli ürünlere “Sale!” rozeti ekler, ancak bu oldukça basit kalır. İndirim yüzdesini gösteren daha detaylı bir rozet ekleyelim:

add_action( 'woocommerce_before_shop_loop_item_title', 'custom_discount_badge', 8 );

function custom_discount_badge() {
    global $product;

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

    // Basit ürün için indirim hesabı
    if ( $product->is_type( 'simple' ) ) {
        $regular_price = (float) $product->get_regular_price();
        $sale_price    = (float) $product->get_sale_price();

        if ( $regular_price > 0 && $sale_price > 0 ) {
            $discount_pct = round( ( ( $regular_price - $sale_price ) / $regular_price ) * 100 );

            echo '<div class="discount-ribbon" style="
                  position:absolute;
                  top:10px;
                  right:10px;
                  background:#e74c3c;
                  color:#fff;
                  padding:5px 10px;
                  border-radius:50%;
                  font-size:14px;
                  font-weight:bold;
                  z-index:10;">
                  %' . intval( $discount_pct ) . '<br>
                  <span style="font-size:10px;">indirim</span>
                  </div>';
        }
    }

    // Variable ürün için
    if ( $product->is_type( 'variable' ) ) {
        $prices         = $product->get_variation_prices( true );
        $regular_prices = $prices['regular_price'];
        $sale_prices    = $prices['sale_price'];

        if ( ! empty( $regular_prices ) && ! empty( $sale_prices ) ) {
            $max_regular = max( $regular_prices );
            $min_sale    = min( $sale_prices );

            if ( $max_regular > 0 ) {
                $discount_pct = round( ( ( $max_regular - $min_sale ) / $max_regular ) * 100 );
                echo '<div class="discount-ribbon" style="
                      position:absolute;
                      top:10px;
                      right:10px;
                      background:#e74c3c;
                      color:#fff;
                      padding:5px 10px;
                      border-radius:50%;
                      font-size:14px;
                      font-weight:bold;">
                      %' . intval( $discount_pct ) . '+<br>
                      <span style="font-size:10px;">indirim</span>
                      </div>';
            }
        }
    }
}

Bu kod woocommerce_before_shop_loop_item_title hook’unu kullandığı için ürün görselinin hemen ardından çalışır. Görselin üzerine absolute pozisyonlama ile rozeti yerleştirmek için parent elementin position:relative olması gerekir. Bunu da CSS ile ayarlayabilirsiniz ya da aşağıdaki gibi JavaScript ile yapabilirsiniz:

// Görselin parent elementine relative eklemek için inline style
add_action( 'wp_head', 'fix_product_card_position' );

function fix_product_card_position() {
    if ( ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
        return;
    }
    echo '<style>
    ul.products li.product { position: relative; }
    .woocommerce-loop-product__link { display: block; }
    </style>';
}

AJAX ile Dinamik İçerik Yükleme

Bazı durumlarda ürün listesinin yavaşlamaması için ağır sorguları AJAX ile asenkron yüklemek isteyebilirsiniz. Örneğin her ürün için “kaç kişi bakıyor” gibi bir bilgiyi gerçek zamanlı göstermek:

// 1. Placeholder HTML'i ekle
add_action( 'woocommerce_after_shop_loop_item', 'add_viewers_placeholder', 20 );

function add_viewers_placeholder() {
    global $product;
    echo '<div class="live-viewers" 
          data-product-id="' . esc_attr( $product->get_id() ) . '"
          style="font-size:12px; color:#e74c3c; min-height:18px;">
          </div>';
}

// 2. AJAX handler ekle
add_action( 'wp_ajax_get_product_viewers', 'ajax_get_product_viewers' );
add_action( 'wp_ajax_nopriv_get_product_viewers', 'ajax_get_product_viewers' );

function ajax_get_product_viewers() {
    check_ajax_referer( 'product_viewers_nonce', 'nonce' );

    $product_id = intval( $_POST['product_id'] );

    if ( ! $product_id ) {
        wp_send_json_error( 'Geçersiz ürün ID' );
    }

    // Gerçek uygulamada bu veriyi Redis, transient veya custom table'dan alırsınız
    $viewers = (int) get_transient( 'product_viewers_' . $product_id );
    if ( ! $viewers ) {
        $viewers = rand( 3, 25 ); // Demo amaçlı random
        set_transient( 'product_viewers_' . $product_id, $viewers, 60 );
    }

    wp_send_json_success( array( 'count' => $viewers ) );
}

// 3. JavaScript'i enqueue et
add_action( 'wp_enqueue_scripts', 'enqueue_product_viewers_script' );

function enqueue_product_viewers_script() {
    if ( ! is_shop() && ! is_product_category() ) {
        return;
    }

    wp_add_inline_script( 'jquery', "
    jQuery(document).ready(function($) {
        $('.live-viewers').each(function() {
            var el = $(this);
            var productId = el.data('product-id');
            $.post('" . admin_url( 'admin-ajax.php' ) . "', {
                action: 'get_product_viewers',
                product_id: productId,
                nonce: '" . wp_create_nonce( 'product_viewers_nonce' ) . "'
            }, function(response) {
                if (response.success && response.data.count > 5) {
                    el.html('👁 ' + response.data.count + ' kişi şu an bakıyor');
                }
            });
        });
    });
    " );
}

Bu yaklaşım performans açısından dikkatli kullanılmalıdır. Sayfada 24 ürün varsa 24 ayrı AJAX isteği tetiklenir. Gerçek bir üretim ortamında tüm ürün ID’lerini tek bir AJAX isteğiyle gönderip toplu sonuç döndürmek çok daha verimli olur.

Sadece Belirli Sayfalarda Çalıştırma

Eklediğiniz içeriklerin sadece ilgili sayfalarda görünmesini sağlamak hem performans hem de görünüm tutarlılığı açısından önemlidir:

add_action( 'woocommerce_after_shop_loop_item', 'conditional_product_content', 15 );

function conditional_product_content() {
    global $product;

    // Sadece shop ve kategori sayfalarında çalış
    if ( ! is_shop() && ! is_product_category() && ! is_product_tag() && ! is_search() ) {
        return;
    }

    // Widget alanlarında veya shortcode ile gösterilen ürünlerde çalışma
    if ( ! wc_get_loop_prop( 'is_shortcode' ) === false ) {
        // is_shortcode true ise bu bir widget/shortcode loop'udur
        // Bunu atlamak isteyebilirsiniz
    }

    $product_id  = $product->get_id();
    $total_sales = (int) get_post_meta( $product_id, 'total_sales', true );

    if ( $total_sales >= 100 ) {
        echo '<div class="bestseller-badge" style="
              background:#f39c12;
              color:#fff;
              text-align:center;
              padding:4px;
              font-size:11px;
              font-weight:bold;
              margin-top:5px;">
              ⭐ ÇOK SATAN ÜRÜN</div>';
    }

    // Yeni ürün kontrolü (30 gün)
    $post_date  = get_the_date( 'U', $product_id );
    $days_old   = ( time() - $post_date ) / DAY_IN_SECONDS;

    if ( $days_old <= 30 ) {
        echo '<div class="new-product-badge" style="
              background:#2ecc71;
              color:#fff;
              text-align:center;
              padding:4px;
              font-size:11px;
              font-weight:bold;
              margin-top:5px;">
              🆕 YENİ ÜRÜN</div>';
    }
}

CSS ile Görünümü Düzenleme

Eklediğiniz içerikler için ayrı bir CSS dosyası kullanmak yerine, doğrudan functions.php üzerinden wp_head hook’una ya da wp_enqueue_styles ile ekleme yapabilirsiniz:

add_action( 'wp_enqueue_scripts', 'custom_product_list_styles' );

function custom_product_list_styles() {
    // Sadece ürün listeleme sayfalarında yükle
    if ( ! is_woocommerce() ) {
        return;
    }

    $custom_css = "
        .custom-product-badge {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 6px 10px;
            margin-top: 8px;
            background-color: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            font-size: 12px;
            color: #495057;
            transition: all 0.2s ease;
        }

        .custom-product-badge:hover {
            background-color: #e9ecef;
        }

        ul.products li.product {
            position: relative;
            overflow: visible;
        }

        .discount-ribbon {
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }

        .stock-warning,
        .stock-ok {
            margin: 4px 0 !important;
            padding: 0;
        }

        @media (max-width: 768px) {
            .custom-product-badge {
                font-size: 11px;
                padding: 4px 8px;
            }
        }
    ";

    wp_register_style( 'custom-product-styles', false );
    wp_enqueue_style( 'custom-product-styles' );
    wp_add_inline_style( 'custom-product-styles', $custom_css );
}

Dikkat Edilmesi Gereken Noktalar

WooCommerce hook’larıyla çalışırken bazı pratik kuralları aklınızda bulundurmanız gerekir:

  • $product global değişkenini her zaman kontrol edin: Hook bazen döngü dışında da tetiklenebilir, if ( ! $product ) return; kontrolü şarttır
  • Öncelik değerlerini iyi planlayın: WooCommerce core’un kullandığı öncelikleri ezmeyin, wc-hooks.php dosyasına bakarak mevcut bağlamları inceleyin
  • Performansı gözleyin: Her ürün için veritabanı sorgusu yapan fonksiyonlar sayfayı yavaşlatır, mümkün olduğunda transient veya object cache kullanın
  • XSS güvenliğine dikkat edin: Kullanıcıdan ya da veritabanından gelen verileri her zaman esc_html(), esc_attr() veya wp_kses_post() ile filtreleyin
  • Child theme’de ya da plugin’de tutun: functions.php değişiklikleri theme güncellemesinde silinir, bu yüzden ya child theme kullanın ya da basit bir custom plugin oluşturun
  • Shortcode ile gösterilen ürün gridlerini test edin: Elementor, Divi gibi page builder’lar kendi ürün listesi widget’larını kullanır ve WooCommerce hook’ları farklı çalışabilir

Değişken Ürünlerde Dikkat Edilecekler

Variable ürünlerde fiyat, stok gibi bilgiler varyasyona göre değiştiğinden, listeleme sayfasında ana ürün üzerinden yapılan hesaplamalar her zaman tam doğru olmayabilir. Bu durumda daha güvenli bir yaklaşım kullanın:

add_action( 'woocommerce_after_shop_loop_item_title', 'safe_variable_product_info', 12 );

function safe_variable_product_info() {
    global $product;

    if ( ! $product ) {
        return;
    }

    if ( $product->is_type( 'variable' ) ) {
        $available_variations = $product->get_available_variations();
        $variation_count      = count( $available_variations );

        echo '<small style="color:#999; display:block; margin-top:3px;">'
             . sprintf(
                 _n( '%d renk/beden seçeneği', '%d renk/beden seçeneği', $variation_count, 'your-textdomain' ),
                 $variation_count
               )
             . '</small>';
    }

    if ( $product->is_type( 'grouped' ) ) {
        $children = $product->get_children();
        echo '<small style="color:#999; display:block; margin-top:3px;">'
             . count( $children ) . ' ürün içerir'
             . '</small>';
    }
}

Sonuç

WooCommerce ürün listesine içerik eklemek, functions.php üzerinden hook sistemiyle son derece esnek ve sürdürülebilir bir şekilde yapılabilir. Stok mesajlarından indirim rozetlerine, kategori bazlı bilgilendirmelerden AJAX ile canlı veri göstermeye kadar pek çok senaryoyu bu yöntemle hayata geçirebilirsiniz.

Temel kuralları şöyle özetleyebiliriz: doğru hook’u seçin, öncelik değerlerini bilinçli kullanın, $product nesnesini her zaman doğrulayın ve güvenlik filtrelerini asla atlamayın. Bunlara ek olarak, tüm bu değişiklikleri bir child theme veya basit bir site-specific plugin içinde tutmak, gelecekteki güncellemelerde başınızı ağrıtmaz.

Ürün listesi sayfası dönüşüm oranınızı artırmak için deneybileceğiniz en hızlı ve en az riskli yol, tam olarak bu hook tabanlı yaklaşımdır. Tema dosyalarını değiştirmeden, eklenti kurmadan, doğrudan PHP ile istediğiniz içeriği doğru yere yerleştirmeniz mümkündür.

Bir yanıt yazın

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