WooCommerce Ürün Sayfasına Özel Alan Ekleme

E-ticaret sitelerinde ürün sayfaları çoğu zaman standart WooCommerce alanlarıyla sınırlı kalır. Oysa gerçek dünyada “Bu ürünün kargo süresi kaç gün?”, “Hangi markadan?”, “Teknik özellikler nerede?” gibi sorular müşterilerin aklını sürekli meşgul eder. İşte tam bu noktada functions.php dosyasına ekleyeceğiniz birkaç fonksiyon, hem müşteri deneyimini hem de sitenizin yönetilebilirliğini ciddi ölçüde artırır. Bu yazıda WooCommerce ürün sayfasına özel alan eklemenin tüm detaylarını, gerçek dünya senaryolarıyla birlikte ele alacağız.

Temel Kavramlar: Meta Kutular ve Custom Field

WooCommerce, WordPress’in meta sistemi üzerine inşa edilmiştir. Bir ürüne özel veri eklemek istediğinizde aslında wp_postmeta tablosuna yeni bir satır yazıyorsunuz. Bu veriyi yönetmek için ürün düzenleme ekranına bir meta kutu eklemeniz, veriyi kaydetmek için bir kayıt fonksiyonu yazmanız ve ürün sayfasında göstermek için de bir görüntüleme fonksiyonu eklemeniz gerekiyor.

Temel akış şöyle işler:

  • add_meta_box(): Admin panelinde ürün düzenleme ekranına giriş alanı ekler
  • update_post_meta(): Girilen değeri veritabanına yazar
  • get_post_meta(): Kaydedilen veriyi çeker
  • woocommerce_single_product_summary: Ürün sayfasında veriyi göstermek için kullanılan hook

Bu dört bileşeni doğru şekilde bir araya getirdiğinizde, tamamen özelleştirilmiş, veri girişine hazır bir alan yapısı elde edersiniz.

İlk Örnek: Basit Bir Metin Alanı

En sık karşılaşılan senaryo, ürüne kısa bir metin notu eklemektir. Mesela “Kargo Süresi” bilgisini eklemek istiyorsunuz. Aşağıdaki kod bloğunu functions.php dosyanıza ekleyebilirsiniz:

// Admin paneline meta kutu ekle
function wc_kargo_suresi_meta_kutusu() {
    add_meta_box(
        'wc_kargo_suresi',
        'Kargo Süresi Bilgisi',
        'wc_kargo_suresi_icerik',
        'product',
        'normal',
        'high'
    );
}
add_action( 'add_meta_boxes', 'wc_kargo_suresi_meta_kutusu' );

// Meta kutusunun içeriği
function wc_kargo_suresi_icerik( $post ) {
    $deger = get_post_meta( $post->ID, '_kargo_suresi', true );
    echo '<label for="kargo_suresi">Tahmini Kargo Süresi:</label>';
    echo '<input type="text" id="kargo_suresi" name="kargo_suresi" value="' . esc_attr( $deger ) . '" style="width:100%;margin-top:8px;" />';
    echo '<p style="color:#666;font-size:12px;">Örnek: 2-3 iş günü</p>';
}

// Kaydedildiğinde veriyi veritabanına yaz
function wc_kargo_suresi_kaydet( $post_id ) {
    if ( isset( $_POST['kargo_suresi'] ) ) {
        update_post_meta( $post_id, '_kargo_suresi', sanitize_text_field( $_POST['kargo_suresi'] ) );
    }
}
add_action( 'save_post', 'wc_kargo_suresi_kaydet' );

Bu kodu kaydettikten sonra admin panelinde herhangi bir ürünü düzenlemeye açtığınızda, “Kargo Süresi Bilgisi” başlıklı yeni bir kutu göreceksiniz.

Ürün Sayfasında Görüntüleme

Meta kutusu ve kayıt fonksiyonu hazır. Şimdi bu veriyi ürün sayfasında gösterme zamanı. WooCommerce’in hook sistemi burada devreye giriyor:

// Ürün sayfasında kargo süresini göster
function wc_kargo_suresi_goster() {
    global $post;
    $kargo_suresi = get_post_meta( $post->ID, '_kargo_suresi', true );

    if ( ! empty( $kargo_suresi ) ) {
        echo '<div class="kargo-suresi-bilgi" style="background:#f9f9f9;padding:10px 15px;margin:15px 0;border-left:4px solid #e02b20;">';
        echo '<strong>Tahmini Kargo Süresi:</strong> ' . esc_html( $kargo_suresi );
        echo '</div>';
    }
}
add_action( 'woocommerce_single_product_summary', 'wc_kargo_suresi_goster', 25 );

Hook’taki 25 sayısı, bilginin sayfada nereye yerleşeceğini belirler. WooCommerce’in varsayılan sıralamasına göre:

  • 5: Ürün başlığı
  • 10: Fiyat
  • 20: Kısa açıklama
  • 25: Bizim alanımız buraya gelir
  • 30: Meta bilgileri (SKU, kategori)
  • 40: Paylaşım butonları

Bu sayıyı değiştirerek içeriğin konumunu kolayca ayarlayabilirsiniz.

Gelişmiş Örnek: Çoklu Alan Desteği

Gerçek projelerde tek bir alan genellikle yetmez. Diyelim ki bir elektronik eşya mağazası yönetiyorsunuz. Her ürün için “Garanti Süresi”, “Marka” ve “Menşei Ülke” alanlarına ihtiyaç var. Bu durumda tek bir meta kutusu içinde birden fazla alan tanımlamak hem daha temiz hem de daha pratiktir:

// Çoklu alan için meta kutusu
function wc_teknik_bilgiler_meta_kutusu() {
    add_meta_box(
        'wc_teknik_bilgiler',
        'Teknik Bilgiler',
        'wc_teknik_bilgiler_icerik',
        'product',
        'normal',
        'default'
    );
}
add_action( 'add_meta_boxes', 'wc_teknik_bilgiler_meta_kutusu' );

function wc_teknik_bilgiler_icerik( $post ) {
    wp_nonce_field( 'wc_teknik_bilgiler_nonce', 'teknik_nonce' );

    $garanti   = get_post_meta( $post->ID, '_garanti_suresi', true );
    $marka     = get_post_meta( $post->ID, '_urun_marka', true );
    $mensee    = get_post_meta( $post->ID, '_mensee_ulke', true );

    echo '<table style="width:100%;border-collapse:collapse;">';

    echo '<tr><td style="padding:8px 0;width:150px;"><label>Garanti Süresi:</label></td>';
    echo '<td><input type="text" name="garanti_suresi" value="' . esc_attr( $garanti ) . '" style="width:100%;" placeholder="Örn: 2 Yıl" /></td></tr>';

    echo '<tr><td style="padding:8px 0;"><label>Marka:</label></td>';
    echo '<td><input type="text" name="urun_marka" value="' . esc_attr( $marka ) . '" style="width:100%;" placeholder="Örn: Samsung" /></td></tr>';

    echo '<tr><td style="padding:8px 0;"><label>Menşei Ülke:</label></td>';
    echo '<td><input type="text" name="mensee_ulke" value="' . esc_attr( $mensee ) . '" style="width:100%;" placeholder="Örn: Türkiye" /></td></tr>';

    echo '</table>';
}

// Kayıt işlemi - nonce doğrulamasıyla
function wc_teknik_bilgiler_kaydet( $post_id ) {
    if ( ! isset( $_POST['teknik_nonce'] ) ) return;
    if ( ! wp_verify_nonce( $_POST['teknik_nonce'], 'wc_teknik_bilgiler_nonce' ) ) return;
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;

    $alanlar = array(
        'garanti_suresi' => '_garanti_suresi',
        'urun_marka'     => '_urun_marka',
        'mensee_ulke'    => '_mensee_ulke',
    );

    foreach ( $alanlar as $post_key => $meta_key ) {
        if ( isset( $_POST[ $post_key ] ) ) {
            update_post_meta( $post_id, $meta_key, sanitize_text_field( $_POST[ $post_key ] ) );
        }
    }
}
add_action( 'save_post', 'wc_teknik_bilgiler_kaydet' );

Burada wp_nonce_field ve wp_verify_nonce kullanımına özellikle dikkat edin. Bu güvenlik katmanı olmadan, harici kaynaklardan gelen sahte POST istekleriyle ürün meta verileri manipüle edilebilir.

Ürün Sayfasında Tablo Görünümü

Birden fazla alan varsa bunları düzenli bir şekilde göstermek önemlidir:

// Teknik bilgileri ürün sayfasında göster
function wc_teknik_bilgiler_goster() {
    global $post;

    $garanti = get_post_meta( $post->ID, '_garanti_suresi', true );
    $marka   = get_post_meta( $post->ID, '_urun_marka', true );
    $mensee  = get_post_meta( $post->ID, '_mensee_ulke', true );

    // En az bir alan doluysa göster
    if ( empty( $garanti ) && empty( $marka ) && empty( $mensee ) ) {
        return;
    }

    echo '<div class="teknik-bilgiler-wrapper" style="margin:20px 0;">';
    echo '<h3 style="font-size:16px;margin-bottom:10px;">Teknik Bilgiler</h3>';
    echo '<ul style="list-style:none;padding:0;margin:0;">';

    if ( ! empty( $marka ) ) {
        echo '<li style="padding:6px 0;border-bottom:1px solid #eee;"><strong>Marka:</strong> ' . esc_html( $marka ) . '</li>';
    }
    if ( ! empty( $garanti ) ) {
        echo '<li style="padding:6px 0;border-bottom:1px solid #eee;"><strong>Garanti:</strong> ' . esc_html( $garanti ) . '</li>';
    }
    if ( ! empty( $mensee ) ) {
        echo '<li style="padding:6px 0;"><strong>Menşei:</strong> ' . esc_html( $mensee ) . '</li>';
    }

    echo '</ul>';
    echo '</div>';
}
add_action( 'woocommerce_single_product_summary', 'wc_teknik_bilgiler_goster', 35 );

Seçim Kutusu (Dropdown) ile Alan Ekleme

Serbest metin yerine belirli seçenekler sunmak istediğinizde dropdown kullanmak daha mantıklıdır. Örneğin bir giyim mağazasında “Üretim Yöntemi” için:

function wc_uretim_yontemi_meta_kutusu() {
    add_meta_box(
        'wc_uretim_yontemi',
        'Üretim Yöntemi',
        'wc_uretim_yontemi_icerik',
        'product',
        'side',
        'default'
    );
}
add_action( 'add_meta_boxes', 'wc_uretim_yontemi_meta_kutusu' );

function wc_uretim_yontemi_icerik( $post ) {
    wp_nonce_field( 'wc_uretim_nonce_action', 'wc_uretim_nonce' );
    $secili = get_post_meta( $post->ID, '_uretim_yontemi', true );

    $secenekler = array(
        ''           => '-- Seçiniz --',
        'el_yapimi'  => 'El Yapımı',
        'fabrika'    => 'Fabrika Üretimi',
        'organik'    => 'Organik Sertifikalı',
        'geri_donusum' => 'Geri Dönüşüm Malzeme',
    );

    echo '<select name="uretim_yontemi" style="width:100%;">';
    foreach ( $secenekler as $deger => $etiket ) {
        $selected = selected( $secili, $deger, false );
        echo '<option value="' . esc_attr( $deger ) . '" ' . $selected . '>' . esc_html( $etiket ) . '</option>';
    }
    echo '</select>';
}

function wc_uretim_yontemi_kaydet( $post_id ) {
    if ( ! isset( $_POST['wc_uretim_nonce'] ) ) return;
    if ( ! wp_verify_nonce( $_POST['wc_uretim_nonce'], 'wc_uretim_nonce_action' ) ) return;
    if ( isset( $_POST['uretim_yontemi'] ) ) {
        update_post_meta( $post_id, '_uretim_yontemi', sanitize_key( $_POST['uretim_yontemi'] ) );
    }
}
add_action( 'save_post', 'wc_uretim_yontemi_kaydet' );

// Ürün sayfasında göster
function wc_uretim_yontemi_goster() {
    global $post;
    $yontem = get_post_meta( $post->ID, '_uretim_yontemi', true );

    $etiketler = array(
        'el_yapimi'    => 'El Yapımı',
        'fabrika'      => 'Fabrika Üretimi',
        'organik'      => 'Organik Sertifikalı',
        'geri_donusum' => 'Geri Dönüşüm Malzeme',
    );

    if ( ! empty( $yontem ) && isset( $etiketler[ $yontem ] ) ) {
        echo '<p class="uretim-bilgi"><strong>Üretim Yöntemi:</strong> ' . esc_html( $etiketler[ $yontem ] ) . '</p>';
    }
}
add_action( 'woocommerce_single_product_summary', 'wc_uretim_yontemi_goster', 28 );

Meta kutusunun 'side' konumuna yerleştirildiğine dikkat edin. Bu, kutunun sağ panel alanında görünmesini sağlar ve editör alanını daha temiz bırakır.

Checkbox ile İkili Durum Alanı

Bazen “Evet/Hayır” türünde basit bir bayrak yeterlidir. Örneğin ürünün “Özel Sipariş” seçeneği sunup sunmadığını işaretlemek için:

function wc_ozel_siparis_meta_kutusu() {
    add_meta_box(
        'wc_ozel_siparis',
        'Özel Sipariş',
        'wc_ozel_siparis_icerik',
        'product',
        'side',
        'low'
    );
}
add_action( 'add_meta_boxes', 'wc_ozel_siparis_meta_kutusu' );

function wc_ozel_siparis_icerik( $post ) {
    wp_nonce_field( 'wc_ozel_siparis_nonce_action', 'wc_ozel_siparis_nonce' );
    $deger = get_post_meta( $post->ID, '_ozel_siparis_var', true );

    echo '<label>';
    echo '<input type="checkbox" name="ozel_siparis_var" value="1" ' . checked( $deger, '1', false ) . ' />';
    echo ' Bu ürün özel sipariş alır';
    echo '</label>';
    echo '<p style="color:#888;font-size:12px;margin-top:8px;">İşaretliyse ürün sayfasında özel sipariş bildirimi gösterilir.</p>';
}

function wc_ozel_siparis_kaydet( $post_id ) {
    if ( ! isset( $_POST['wc_ozel_siparis_nonce'] ) ) return;
    if ( ! wp_verify_nonce( $_POST['wc_ozel_siparis_nonce'], 'wc_ozel_siparis_nonce_action' ) ) return;

    $deger = isset( $_POST['ozel_siparis_var'] ) ? '1' : '0';
    update_post_meta( $post_id, '_ozel_siparis_var', $deger );
}
add_action( 'save_post', 'wc_ozel_siparis_kaydet' );

// Ürün sayfasında göster
function wc_ozel_siparis_goster() {
    global $post;
    $ozel = get_post_meta( $post->ID, '_ozel_siparis_var', true );

    if ( $ozel === '1' ) {
        echo '<div style="background:#fff8e1;border:1px solid #ffc107;padding:10px;margin:10px 0;border-radius:4px;">';
        echo '<strong>Özel Sipariş:</strong> Bu ürün özel sipariş alınabilir. Bizimle iletişime geçin.';
        echo '</div>';
    }
}
add_action( 'woocommerce_single_product_summary', 'wc_ozel_siparis_goster', 22 );

Checkbox’larda dikkat edilmesi gereken kritik bir nokta var: Form gönderildiğinde işaretlenmemiş bir checkbox hiç POST verisi göndermez. Bu yüzden kayıt fonksiyonunda isset() ile kontrol edip her iki durumu da açıkça kaydetmek önemlidir.

WooCommerce Sekmeleri ile Alanı Gösterme

Ürün sayfasındaki özet alanı (summary) yerine, uzun içerikleri “Açıklama”, “İncelemeler” sekmelerinin yanına yeni bir sekme ekleyerek göstermek çok daha düzenli görünür:

// Yeni sekme ekle
function wc_ozel_sekme_ekle( $tabs ) {
    global $post;
    $icerik = get_post_meta( $post->ID, '_kurulum_talimatlari', true );

    if ( ! empty( $icerik ) ) {
        $tabs['kurulum'] = array(
            'title'    => 'Kurulum Talimatları',
            'priority' => 50,
            'callback' => 'wc_kurulum_sekme_icerik',
        );
    }
    return $tabs;
}
add_filter( 'woocommerce_product_tabs', 'wc_ozel_sekme_ekle' );

function wc_kurulum_sekme_icerik() {
    global $post;
    $icerik = get_post_meta( $post->ID, '_kurulum_talimatlari', true );
    echo '<h2>Kurulum Talimatları</h2>';
    echo wp_kses_post( wpautop( $icerik ) );
}

// Admin meta kutusu
function wc_kurulum_meta_kutusu() {
    add_meta_box(
        'wc_kurulum',
        'Kurulum Talimatları',
        'wc_kurulum_meta_icerik',
        'product',
        'normal',
        'low'
    );
}
add_action( 'add_meta_boxes', 'wc_kurulum_meta_kutusu' );

function wc_kurulum_meta_icerik( $post ) {
    wp_nonce_field( 'wc_kurulum_nonce_action', 'wc_kurulum_nonce' );
    $icerik = get_post_meta( $post->ID, '_kurulum_talimatlari', true );
    echo '<textarea name="kurulum_talimatlari" rows="8" style="width:100%;">' . esc_textarea( $icerik ) . '</textarea>';
    echo '<p style="color:#888;font-size:12px;">Bu alan ürün sayfasında "Kurulum Talimatları" sekmesi olarak görünür.</p>';
}

function wc_kurulum_kaydet( $post_id ) {
    if ( ! isset( $_POST['wc_kurulum_nonce'] ) ) return;
    if ( ! wp_verify_nonce( $_POST['wc_kurulum_nonce'], 'wc_kurulum_nonce_action' ) ) return;
    if ( isset( $_POST['kurulum_talimatlari'] ) ) {
        update_post_meta( $post_id, '_kurulum_talimatlari', wp_kses_post( $_POST['kurulum_talimatlari'] ) );
    }
}
add_action( 'save_post', 'wc_kurulum_kaydet' );

Bu yapı, özellikle teknik ürün satan sitelerde çok işe yarıyor. Elektrikli aletler, yazılım ürünleri, montaj gerektiren mobilyalar gibi alanlarda kurulum bilgisi ayrı bir sekme olarak sunulduğunda kullanıcı deneyimi ciddi ölçüde iyileşiyor.

Güvenlik ve Performans Notları

Tüm bu kodları yazarken göz ardı edilmemesi gereken birkaç önemli nokta var:

  • Nonce doğrulaması: Her form işleminde wp_nonce_field ve wp_verify_nonce kullanın. Bu, CSRF saldırılarına karşı temel korumadır
  • Veri sanitizasyonu: Metin için sanitize_text_field(), HTML içeren alanlar için wp_kses_post(), URL için esc_url_raw() kullanın
  • Çıktı kaçırma (escaping): Gösterim sırasında her zaman esc_html(), esc_attr() veya esc_url() kullanın
  • Autosave kontrolü: save_post hook’unda DOING_AUTOSAVE sabitini kontrol etmek gereksiz veritabanı yazımlarını önler
  • İzin kontrolü: current_user_can( 'edit_post', $post_id ) ile kullanıcının bu işlemi yapma yetkisi olup olmadığını kontrol edin
  • Boş değer kontrolü: Görüntüleme fonksiyonlarında her zaman empty() kontrolü yaparak boş HTML blokları oluşturmaktan kaçının

Yaygın Hatalar ve Çözümleri

Uygulamada sık karşılaşılan sorunları önceden bilmek çok zaman kazandırır:

  • Alan kaydedilmiyor: save_post hook’u mu yoksa WooCommerce’in kendi hook’larından birini mi kullanmanız gerektiğini kontrol edin. Özellikle WooCommerce 3.x ve sonrasında bazen woocommerce_process_product_meta hook’u daha güvenilir çalışır
  • Alan her kayıtta sıfırlanıyor: Nonce alanının formda bulunduğundan emin olun; bazen yüklenen başka bir plugin çakışması olabilir
  • Değer ürün sayfasında görünmüyor: Global $post değişkeninin doğru product ID’yi taşıdığını var_dump($post->ID) ile test edin. Daha güvenli alternatif olarak get_the_ID() kullanabilirsiniz
  • HTML entity sorunları: Admin ekranında &, " gibi karakterler görüyorsanız, meta değerini çekerken html_entity_decode() uygulayabilirsiniz ancak asıl çözüm kayıt sırasında doğru sanitizasyon yapmaktır

WooCommerce’in Kendi Panel Sekmelerine Entegrasyon

Meta kutusu yerine WooCommerce’in kendi ürün düzenleme paneline (Genel, Stok, Nakliye sekmeleri gibi) alan eklemek de mümkündür. Bu yaklaşım, daha native bir admin deneyimi sunar:

// WooCommerce "Genel" sekmesine alan ekle
function wc_panel_alani_ekle() {
    echo '<div class="options_group">';
    woocommerce_wp_text_input( array(
        'id'          => '_ozel_model_no',
        'label'       => 'Model Numarası',
        'placeholder' => 'Örn: XR-2024-TRK',
        'desc_tip'    => true,
        'description' => 'Üreticinin belirlediği model numarasını girin.',
    ) );
    echo '</div>';
}
add_action( 'woocommerce_product_options_general_product_data', 'wc_panel_alani_ekle' );

// Kayıt
function wc_panel_alani_kaydet( $post_id ) {
    $model_no = isset( $_POST['_ozel_model_no'] ) ? sanitize_text_field( $_POST['_ozel_model_no'] ) : '';
    update_post_meta( $post_id, '_ozel_model_no', $model_no );
}
add_action( 'woocommerce_process_product_meta', 'wc_panel_alani_kaydet' );

woocommerce_wp_text_input() fonksiyonu, WooCommerce’in kendi stil ve yapısına uygun bir giriş alanı oluşturur. Benzer şekilde woocommerce_wp_select(), woocommerce_wp_checkbox() ve woocommerce_wp_textarea_input() fonksiyonları da mevcuttur. Bu yöntemi kullandığınızda alanlar Genel, Stok veya Nakliye sekmelerinin altına eklenir ve görsel olarak WooCommerce’in geri kalanıyla bütünleşir.

Sonuç

WooCommerce ürün sayfasına özel alan eklemek ilk bakışta karmaşık görünse de aslında birkaç temel prensibin tekrarından ibareti. Meta kutu oluştur, veriyi güvenli şekilde kaydet, ürün sayfasında uygun hook ile göster. Bu üç adımı doğru kurguladığınızda, ister tek bir metin kutusu, ister çoklu alan grupları, isterse sekme tabanlı uzun içerikler olsun, tüm senaryolara uygulanabilir bir altyapı elde edersiniz.

functions.php içindeki bu tür özelleştirmeler zamanla birikince, kodları bir inc/ klasörüne taşımayı ve require_once ile dahil etmeyi düşünün. Büyüyen projelerde bu küçük organizasyonel adım, uzun vadede bakım maliyetini ciddi ölçüde düşürür. Ayrıca site temasını değiştirmeniz gerektiğinde tüm bu özelleştirmeleri kaybetmemek için child theme kullanımını alışkanlık haline getirmenizi önemle tavsiye ederim.

Bir yanıt yazın

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