WooCommerce Ürün Sayfasının Başına İçerik Ekleme
E-ticaret sitenizi yönetirken er ya da geç şu soruyla karşılaşırsınız: “Ürün sayfasının en üstüne bir şeyler eklemem lazım ama tema dosyalarını bozmak istemiyorum.” Belki bir kampanya duyurusu, belki kargo bilgisi, belki de özel bir uyarı mesajı. WooCommerce bu tür özelleştirmeler için hook sistemi sunar ve doğru hook’u kullanarak functions.php üzerinden tema dosyalarına hiç dokunmadan istediğiniz içeriği ekleyebilirsiniz.
Bu yazıda WooCommerce ürün sayfasının başına içerik eklemenin farklı yollarını, gerçek dünya senaryolarıyla birlikte ele alacağız.
WooCommerce Hook Sistemi ve Ürün Sayfası Yapısı
WooCommerce, ürün sayfasını oluştururken onlarca action hook kullanır. Bu hook’lar sayesinde tema dosyalarını düzenlemeden sayfanın istediğiniz bölümüne içerik ekleyebilirsiniz. Ürün sayfasının başına içerik eklemek için en çok kullanılan hook şunlardır:
- woocommerce_before_single_product: Ürün container’ının en üstüne ekler
- woocommerce_before_single_product_summary: Ürün görselinin ve özet alanının hemen üstüne ekler
- woocommerce_single_product_summary: Özet alanının içine ekler (öncelik sırasıyla)
- woocommerce_before_add_to_cart_form: Sepete ekle butonundan hemen önce ekler
Hangi hook’u kullanacağınız, içeriğinizin sayfada nerede görünmesini istediğinize bağlıdır. Şimdi bunları tek tek inceleyelim.
Temel Kullanım: En Basit İçerik Ekleme
İlk örneğimizle başlayalım. Tüm ürün sayfalarının üstüne basit bir duyuru mesajı ekleyelim:
// functions.php dosyasına ekleyin
add_action( 'woocommerce_before_single_product', 'custom_product_page_notice' );
function custom_product_page_notice() {
echo '<div class="custom-notice" style="background: #fff3cd; padding: 10px 15px; margin-bottom: 15px; border-left: 4px solid #ffc107;">
<strong>Dikkat!</strong> Bu ürün için ücretsiz kargo geçerlidir.
</div>';
}
Bu kod çalışır ama üretim ortamı için biraz ham. Inline CSS kullanmak yerine bir CSS sınıfı tanımlayıp stil dosyasına veya wp_head hook’una eklemek daha temiz olur.
Öncelik Parametresini Doğru Kullanmak
add_action fonksiyonunun üçüncü parametresi olan öncelik değeri, birden fazla içerik eklediğinizde hangisinin önce görüneceğini belirler. Düşük sayı, daha önce çalışır anlamına gelir:
// Önce bu çalışır (öncelik: 5)
add_action( 'woocommerce_before_single_product_summary', 'show_campaign_banner', 5 );
function show_campaign_banner() {
$campaign_active = get_option( 'my_campaign_active', false );
if ( ! $campaign_active ) {
return;
}
echo '<div class="campaign-banner">
<p>🎉 Bugüne özel %20 indirim fırsatı! Kodu kullanın: <strong>INDIRIM20</strong></p>
</div>';
}
// Sonra bu çalışır (öncelik: 10)
add_action( 'woocommerce_before_single_product_summary', 'show_stock_warning', 10 );
function show_stock_warning() {
global $product;
if ( $product->get_stock_quantity() < 5 && $product->get_stock_quantity() > 0 ) {
echo '<div class="stock-warning">
⚠️ Son <strong>' . $product->get_stock_quantity() . '</strong> ürün kaldı!
</div>';
}
}
Belirli Kategorilere Özel İçerik Ekleme
Gerçek dünyada genellikle tüm ürünlere değil, belirli kategorilerdeki ürünlere özel içerik eklemek istersiniz. Örneğin sadece “elektronik” kategorisindeki ürünler için garanti bilgisi göstermek:
add_action( 'woocommerce_before_single_product_summary', 'category_specific_content', 8 );
function category_specific_content() {
global $product;
// Ürün belirli bir kategoride mi kontrol et
$product_id = $product->get_id();
// Elektronik kategorisi için garanti bilgisi
if ( has_term( 'elektronik', 'product_cat', $product_id ) ) {
echo '<div class="warranty-info" style="background: #e8f4f8; padding: 12px; margin-bottom: 20px; border-radius: 4px;">
<span>🛡️</span>
<strong>2 Yıl Resmi Garanti</strong> - Tüm elektronik ürünlerimiz yetkili servis garantisi kapsamındadır.
</div>';
}
// Gıda kategorisi için son kullanma tarihi uyarısı
if ( has_term( 'gida', 'product_cat', $product_id ) ) {
echo '<div class="food-warning" style="background: #fef9e7; padding: 12px; margin-bottom: 20px; border-radius: 4px;">
<span>📦</span>
Tüm gıda ürünlerimiz minimum 3 ay son kullanma tarihiyle gönderilmektedir.
</div>';
}
}
Özel Ürün Meta Verisi Kullanarak Dinamik İçerik
Bazen her ürün için farklı bir içerik göstermek istersiniz. Bu durumda ürün meta verisini kullanabilirsiniz. Diyelim ki bazı ürünler için “özel üretim süresi” bilgisi göstermek istiyorsunuz:
add_action( 'woocommerce_before_single_product_summary', 'show_production_time_notice', 7 );
function show_production_time_notice() {
global $product;
$production_days = get_post_meta( $product->get_id(), '_production_days', true );
if ( empty( $production_days ) ) {
return;
}
$message = sprintf(
'Bu ürün sipariş üzerine üretilmektedir. Tahmini hazırlık süresi: <strong>%d iş günü</strong>',
intval( $production_days )
);
echo '<div class="production-notice">';
echo '<div class="notice-icon">🔨</div>';
echo '<div class="notice-text">' . $message . '</div>';
echo '</div>';
}
Bu meta veriyi ürün düzenleme ekranına da ekleyebilirsiniz. Böylece içerik yöneticiniz kod yazmadan her ürün için bu değeri ayarlayabilir:
// Ürün düzenleme ekranına özel alan ekle
add_action( 'woocommerce_product_options_general_product_data', 'add_production_days_field' );
function add_production_days_field() {
woocommerce_wp_text_input(
array(
'id' => '_production_days',
'label' => 'Üretim Süresi (gün)',
'placeholder' => 'Örn: 5',
'description' => 'Bu ürün için sipariş sonrası gerekli hazırlık süresi',
'type' => 'number',
)
);
}
// Kaydedildiğinde meta veriyi kaydet
add_action( 'woocommerce_process_product_meta', 'save_production_days_field' );
function save_production_days_field( $post_id ) {
$production_days = isset( $_POST['_production_days'] ) ? sanitize_text_field( $_POST['_production_days'] ) : '';
update_post_meta( $post_id, '_production_days', $production_days );
}
Kullanıcı Rolüne Göre Farklı İçerik Gösterme
B2B e-ticaret sitelerinde sık karşılaşılan bir senaryo: Toptan alıcılara farklı, bireysel müşterilere farklı bilgi göstermek. Örneğin toptan müşterilere birim fiyat göstermek:
add_action( 'woocommerce_before_single_product_summary', 'show_wholesale_info', 6 );
function show_wholesale_info() {
// Kullanıcı giriş yapmamışsa gösterme
if ( ! is_user_logged_in() ) {
echo '<div class="login-prompt" style="background: #f0f0f0; padding: 12px; margin-bottom: 20px;">
<a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '">Giriş yapın</a>
ve toptan fiyat avantajlarından yararlanın.
</div>';
return;
}
$user = wp_get_current_user();
// Kullanıcı toptan müşteri mi?
if ( in_array( 'wholesale_customer', (array) $user->roles ) ) {
global $product;
$price = $product->get_price();
$bulk_price = $price * 0.80; // %20 indirim
echo '<div class="wholesale-badge" style="background: #d4edda; padding: 12px; margin-bottom: 20px; border-left: 4px solid #28a745;">';
echo '<strong>🏢 Toptan Fiyat:</strong> ' . wc_price( $bulk_price ) . ' (10+ adet siparişlerde)';
echo '</div>';
}
}
Stok Durumuna Göre Dinamik Bildirim Sistemi
Stok yönetimi kritik olan mağazalar için daha kapsamlı bir örnek. Bu kod parçası ürünün stok durumuna göre farklı mesajlar gösterir ve aciliyet hissi yaratır:
add_action( 'woocommerce_before_single_product_summary', 'advanced_stock_notification', 9 );
function advanced_stock_notification() {
global $product;
// Stok takibi kapalıysa çık
if ( ! $product->managing_stock() ) {
return;
}
$stock_qty = $product->get_stock_quantity();
$stock_status = $product->get_stock_status();
if ( $stock_status === 'outofstock' ) {
// Stok yok - geri bildirim formu linki
echo '<div class="out-of-stock-notice" style="background: #f8d7da; padding: 15px; margin-bottom: 20px; border-radius: 4px;">';
echo '<strong>😔 Bu ürün şu an stokta yok.</strong><br>';
echo '<small>Stoka geldiğinde haber almak için ';
echo '<a href="#notify-form">buraya tıklayın</a>.</small>';
echo '</div>';
} elseif ( $stock_qty !== null && $stock_qty <= 3 ) {
// Kritik stok seviyesi
echo '<div class="critical-stock" style="background: #fff3cd; padding: 15px; margin-bottom: 20px; border-radius: 4px; animation: pulse 2s infinite;">';
echo '🔥 <strong>Son ' . $stock_qty . ' ürün!</strong> ';
echo 'Bu ürün çok talep görüyor, kaçırmayın!';
echo '</div>';
} elseif ( $stock_qty !== null && $stock_qty <= 10 ) {
// Düşük stok uyarısı
echo '<div class="low-stock" style="background: #fff8e1; padding: 12px; margin-bottom: 20px; border-radius: 4px;">';
echo '⚡ <strong>Sınırlı stok</strong> - Sadece ' . $stock_qty . ' adet kaldı.';
echo '</div>';
}
}
Zaman Bazlı İçerik: Kampanya Sayacı
Belirli tarihler arasında çalışan, otomatik olarak açılıp kapanan içerik eklemek için:
add_action( 'woocommerce_before_single_product', 'time_based_campaign_notice' );
function time_based_campaign_notice() {
// Kampanya tarihleri (site saat dilimine göre)
$campaign_start = strtotime( '2024-11-29 00:00:00' );
$campaign_end = strtotime( '2024-12-02 23:59:59' );
$current_time = current_time( 'timestamp' );
// Kampanya dışındaysa gösterme
if ( $current_time < $campaign_start || $current_time > $campaign_end ) {
return;
}
// Kampanya bitimine kalan süre
$remaining = $campaign_end - $current_time;
$hours = floor( $remaining / 3600 );
$minutes = floor( ( $remaining % 3600 ) / 60 );
echo '<div class="black-friday-banner" style="background: #1a1a1a; color: #ffd700; padding: 15px; margin-bottom: 20px; text-align: center; font-weight: bold;">';
echo '🛍️ BLACK FRIDAY FIRSATI! ';
echo 'Kampanya bitimine: <span id="countdown">' . $hours . ' saat ' . $minutes . ' dakika</span> kaldı.';
echo '</div>';
// Basit JS sayacı
echo '<script>
(function() {
var endTime = ' . $campaign_end . ' * 1000;
function updateCountdown() {
var now = new Date().getTime();
var diff = endTime - now;
if (diff <= 0) {
document.getElementById("countdown").innerHTML = "Kampanya sona erdi";
return;
}
var h = Math.floor(diff / 3600000);
var m = Math.floor((diff % 3600000) / 60000);
var s = Math.floor((diff % 60000) / 1000);
document.getElementById("countdown").innerHTML = h + " saat " + m + " dk " + s + " sn";
setTimeout(updateCountdown, 1000);
}
updateCountdown();
})();
</script>';
}
Transient Kullanarak Performansı Optimize Etme
Eğer eklediğiniz içerik veritabanı sorgusu gerektiriyorsa, her sayfa yüklemesinde aynı sorguyu çalıştırmak yerine WordPress transient sistemi kullanabilirsiniz:
add_action( 'woocommerce_before_single_product_summary', 'show_related_reviews_count', 8 );
function show_related_reviews_count() {
global $product;
$product_id = $product->get_id();
// Transient cache kontrol et (1 saat geçerli)
$cache_key = 'product_review_stats_' . $product_id;
$review_stats = get_transient( $cache_key );
if ( false === $review_stats ) {
// Cache yoksa hesapla
$comments = get_comments( array(
'post_id' => $product_id,
'status' => 'approve',
'type' => 'review',
) );
$total = count( $comments );
$avg = $product->get_average_rating();
$review_stats = array(
'total' => $total,
'avg' => $avg,
);
// 1 saat cache'le
set_transient( $cache_key, $review_stats, HOUR_IN_SECONDS );
}
if ( $review_stats['total'] > 0 ) {
echo '<div class="review-summary-bar" style="background: #f8f9fa; padding: 10px 15px; margin-bottom: 15px; border-radius: 4px;">';
echo '⭐ <strong>' . number_format( $review_stats['avg'], 1 ) . '/5</strong> ';
echo '(' . $review_stats['total'] . ' değerlendirme) - ';
echo '<a href="#reviews">Yorumları gör</a>';
echo '</div>';
}
// Ürün güncellendiğinde cache'i temizle
add_action( 'save_post_product', function( $post_id ) use ( $cache_key ) {
delete_transient( $cache_key );
});
}
CSS ile Bildirimleri Düzgün Görüntüleme
Tüm bu bildirimleri düzgün göstermek için tutarlı bir CSS yapısı kullanmak önemli. Bu kodları doğrudan inline yazmak yerine wp_enqueue_style ile yüklemek daha temiz bir yaklaşım:
add_action( 'wp_enqueue_scripts', 'enqueue_product_notice_styles' );
function enqueue_product_notice_styles() {
// Sadece ürün sayfalarında yükle
if ( ! is_product() ) {
return;
}
$custom_css = '
.product-notice {
padding: 12px 16px;
margin-bottom: 20px;
border-radius: 4px;
font-size: 14px;
line-height: 1.5;
display: flex;
align-items: center;
gap: 10px;
}
.product-notice--info {
background-color: #e8f4f8;
border-left: 4px solid #17a2b8;
color: #0c5460;
}
.product-notice--warning {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
color: #856404;
}
.product-notice--success {
background-color: #d4edda;
border-left: 4px solid #28a745;
color: #155724;
}
.product-notice--danger {
background-color: #f8d7da;
border-left: 4px solid #dc3545;
color: #721c24;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
.critical-stock {
animation: pulse 2s infinite;
}
';
wp_register_style( 'product-notices', false );
wp_enqueue_style( 'product-notices' );
wp_add_inline_style( 'product-notices', $custom_css );
}
Hata Ayıklama ve Yaygın Sorunlar
Kodunuzu yazarken karşılaşabileceğiniz yaygın sorunlar ve çözümleri:
- İçerik görünmüyor: Yanlış hook kullanıyor olabilirsiniz.
woocommerce_before_single_producthook’u ürünün container div’inden önce tetiklenir, bazı temalarda bu div dışında kalabilir.
- $product global çalışmıyor:
global $product;satırını fonksiyonun içinde tanımlamayı unutmayın. WooCommerce bazı durumlarda bu global’i otomatik set etmeyebilir.
- Hook çift çalışıyor: Fonksiyonunuzu bir sınıf içinde kullanıyorsanız ve hem constructor’da hem de doğrudan çağırıyorsanız çift çalışabilir.
has_action()kontrolü ekleyin.
- Önbellek sorunu: WP Rocket, W3 Total Cache gibi eklentiler sayfa önbelleğe alındığında dinamik içerikleriniz güncel görünmeyebilir. Fragment caching veya AJAX kullanmayı değerlendirin.
- Tema uyumluluğu: Bazı premium temalar WooCommerce hook’larını kaldırır veya değiştirir. Bu durumda temaya özel hook’ları araştırmanız gerekir.
// Hangi hook'ların çalıştığını test etmek için
add_action( 'all', function( $tag ) {
if ( is_product() && strpos( $tag, 'woocommerce' ) !== false ) {
error_log( 'Hook: ' . $tag );
}
});
Bu debug kodunu geçici olarak kullanıp error logdan çalışan hook’ları görebilirsiniz. Kesinlikle canlı ortamda bırakmayın.
Güvenlik Kontrolleri
İçerik eklerken güvenliği asla ihmal etmeyin:
add_action( 'woocommerce_before_single_product_summary', 'secure_custom_content', 10 );
function secure_custom_content() {
global $product;
// Geçerli bir WooCommerce ürünü mü?
if ( ! $product instanceof WC_Product ) {
return;
}
// Kullanıcıdan gelen veriyi her zaman sanitize edin
$custom_message = get_post_meta( $product->get_id(), '_custom_notice', true );
if ( ! empty( $custom_message ) ) {
// wp_kses ile güvenli HTML etiketleri izin ver
$allowed_html = array(
'strong' => array(),
'em' => array(),
'a' => array( 'href' => array(), 'title' => array() ),
'br' => array(),
);
echo '<div class="product-notice product-notice--info">';
echo wp_kses( $custom_message, $allowed_html );
echo '</div>';
}
}
Sonuç
WooCommerce’in hook sistemi, ürün sayfanızı özelleştirmek için inanılmaz derecede güçlü bir araç. woocommerce_before_single_product ve woocommerce_before_single_product_summary hook’larını doğru önceliklerle kullanarak tema dosyalarına hiç dokunmadan sayfanıza kampanya banner’ı, stok uyarısı, garanti bilgisi, kullanıcı rolüne özel içerik gibi her türlü dinamik bilgiyi ekleyebilirsiniz.
Önemli olan birkaç nokta var: Her zaman is_product() kontrolü yapın, global $product nesnesini fonksiyon içinde tanımlayın, kullanıcıdan gelen verileri mutlaka sanitize edin ve yoğun veritabanı sorguları için transient cache kullanın. Performans açısından bakıldığında, tüm bu bildirimleri sadece ürün sayfalarında yüklemek için koşullu etiketleri kullanmak önemli.
Bu yaklaşımın en büyük avantajı güncelleme güvenliği. Tema veya WooCommerce güncellemelerinden etkilenmeden özelleştirmeleriniz çalışmaya devam eder. Üstelik child theme kullanıyorsanız bu kodları oraya koyduğunuzda parent tema güncellendiğinde değişiklikleriniz kaybolmaz.
Bir sonraki adım olarak bu kodları bir mini eklentiye dönüştürmeyi düşünebilirsiniz. wp-content/plugins dizininde küçük bir eklenti dosyası oluşturmak, kodlarınızı daha düzenli tutar ve herhangi bir temaya geçişte taşımanızı kolaylaştırır.
