Fiyatlandırma ve teklif

Eklentisiz WooCommerce katalog modu: kod, kapsam ve sızıntılar

Katalog modu tek bir anahtar değil, birbirinden bağımsız üç hook: üç filtre ve iki remove_action çağrısı, tamamı yapıştırmaya hazır. Koddan sonra çoğu rehberin atladığı kısım geliyor — Store API, varyasyon JSON'ı, JSON-LD ve REST gibi fiyat sızdırmayı sürdüren yüzeyler.

Toptancı müşterilere satıyorsunuz, ya da fiyatlarınız sözleşmeden sözleşmeye değişiyor, ya da bir rakibin gece 3’te kataloğunuzu kazımasını istemiyorsunuz. Bu yüzden mağazanın katalog olarak çalışmayı sürdürmesini istiyorsunuz: ürünler, kategoriler, arama, görseller yerinde kalsın; fiyat görünmesin, satın almanın yolu olmasın. Üstelik bunun için bir eklenti daha kurmak istemiyorsunuz.

Bu makul bir istek ve pek çok mağaza için aşağıdaki kod yeterli. Aşağıda eklenti bağımlılığı olmadan WooCommerce katalog modu var: üç filtre ve iki remove_action çağrısı, tamamı, yapıştırmaya hazır. Koddan sonra çoğu rehberin atladığı kısım geliyor: mağaza vitrini temiz göründükten çok sonra bile yalnızca snippet’e dayanan yaklaşımın fiyat dağıtmayı sürdürdüğü yüzeyler.

Katalog modunun gerçekte anlattığı üç şey#

İnsanlar “katalog modu” derken birbirinden biraz farklı şeyler kastediyor. Kod yazmadan önce bunlardan hangisine ihtiyacınız olduğuna karar verin, çünkü her biri ayrı bir hook:

  • Satın alma yok. Ürün hiçbir yoldan sepete eklenemez.
  • Sepete Ekle düğmesi yok. Düğme hem mağaza döngüsünden hem ürün sayfasından kalkar, kimsenin kafası karışmaz.
  • Fiyat yok. Fiyatın yerini bir etiket alır ya da fiyat tamamen kaldırılır.

İstediğiniz bileşimi kurabilirsiniz. Bazı mağazalar fiyatları bırakıp yalnızca satın almayı kaldırır; telefonla sipariş alan bir showroom gibi. Bazıları fiyatları gizler ama oturum açmış toptan hesaplar için sepeti açık tutar. Eklentisiz WooCommerce katalog modu tek bir anahtar değil, birbirinden bağımsız üç hook. Bu yüzden hiçbir şey yapıştırmadan önce üçünden hangisini istediğinizi yazın. Yanlış bileşim size üzerinde Satın Al düğmesi olan ama fiyatı olmayan bir ürün sayfası bırakır.

Aşağıdaki kodun tamamı alt temanızın functions.php dosyasına ya da daha iyisi wp-content/plugins/ içindeki küçük, siteye özel bir eklentiye girer. Ana temayı düzenlemeyin; bir sonraki güncelleme yazdığınızı siler.

Adım 1: ürünleri satın alınamaz hale getirin#

Yükü taşıyan filtre bu. woocommerce_is_purchasable filtresi WC_Product::is_purchasable() içinde uygulanır ve WC_Cart::add_to_cart() herhangi bir şeyi kabul etmeden önce bu metodu çağırır. Dolayısıyla burada false döndürmek yalnızca bir düğmeyi gizlemez; birisi isteği elle hazırlasa bile ekleme başarısız olur.

add_filter( 'woocommerce_is_purchasable', '__return_false' );
add_filter( 'woocommerce_variation_is_purchasable', '__return_false' );

Bir değil, iki filtre. Varyasyonlar WC_Product_Variation::is_purchasable() içinde kendi filtrelerinden geçer; ikincisini unutmak, “çalışan” bir katalog modunun varyasyonlu bir ürünün belirli bir bedenini hâlâ satın aldırmasının sık görülen nedenidir.

Katalog modunu mağazanın yalnızca bir bölümünde istiyorsanız __return_false yerine ürün argümanını alın:

add_filter( 'woocommerce_is_purchasable', function ( $purchasable, $product ) {
    // Catalog mode only for the "trade" category.
    if ( has_term( 'trade', 'product_cat', $product->get_id() ) ) {
        return false;
    }
    return $purchasable;
}, 10, 2 );

Bu sürüm basit ürünler ve varyasyonlu ana ürünler için doğrudur. Varyasyonlar için doğru değildir: bir varyasyonda $product->get_id() varyasyonun kendi ID’sini döndürür ve varyasyonlar product_cat terimleri taşımaz, dolayısıyla has_term() her zaman false döner. Varyasyon filtresinde bunun yerine $product->get_parent_id() ile ana ürüne sorun. Kategori hedeflemenin bundan başka tuzakları da var; hepsi belirli ürünler veya kategoriler için fiyat gizleme yazısında ele alınıyor.

Adım 2: Sepete Ekle düğmesini şablonlardan kaldırın#

Bir ürünü satın alınamaz yapmak, çoğu klasik temada standart düğmeyi zaten bastırır; çünkü çekirdek şablonlar bir şey basmadan önce is_purchasable() denetimi yapar. Ama temalar şablonları sürekli geçersiz kılıyor ve kendi düğme işaretlemesini basan bir tema basmayı sürdürür. Aksiyonları açıkça kaldırın:

add_action( 'init', function () {
    // Shop loop, related products, up-sells, cross-sells.
    remove_action(
        'woocommerce_after_shop_loop_item',
        'woocommerce_template_loop_add_to_cart',
        10
    );

    // Single product page.
    remove_action(
        'woocommerce_single_product_summary',
        'woocommerce_template_single_add_to_cart',
        30
    );
} );

Öncelik numaraları önemli. remove_action() yalnızca adını verdiğiniz öncelikte kayıtlı olan geri çağrımı kaldırır ve WooCommerce bunları sırasıyla 10 ile 30 önceliğinde kaydeder. Numarayı yanlış verirseniz çağrı sessizce hiçbir şey yapmaz: uyarı yok, hata yok, yalnızca yerinde duran bir düğme. Bir tema geri çağrımı başka bir önceliğe taşımışsa, bir hazırlık sitesinde global $wp_filter; print_r( $wp_filter['woocommerce_single_product_summary'] ); ile bulun.

init sarmalayıcısı, kaldırmanın WooCommerce kendi şablon hook’larını kaydettikten sonra çalışması için orada. Bir temanın functions.php dosyasından zaten yeterince geç çalışıyorsunuz; önce yüklenen siteye özel bir eklentiden ise çalışmıyorsunuz ve henüz eklenmemiş bir geri çağrıma karşı işletilen remove_action hiçbir şey kaldırmaz.

Bu tek hook’ta göründüğünden fazla ayrıntı var. Temalar, blok şablonları ve varyasyon formu birbirinden farklı davranır. Konu WooCommerce’te Sepete Ekle düğmesini gizleme yazısında hakkıyla açılıyor.

Adım 3: fiyatı boşaltın#

Klasik şablonlarda katalog fiyatları woocommerce_get_price_html üzerinden geçer. Tek bir filtre; tekil ürün sayfasını, mağaza döngüsünü, ilgili ürünleri ve varyasyonlu ürünün fiyat aralığını birlikte kapsar:

add_filter( 'woocommerce_get_price_html', function ( $price_html, $product ) {
    return '<span class="price-on-request">'
         . esc_html__( 'Price on request', 'your-textdomain' )
         . '</span>';
}, 100, 2 );

Öncelik 10 değil 100; böylece para birimi değiştiricilerden, indirim rozeti eklentilerinden ve fiyat HTML’ini filtreleyen başka her şeyden sonra çalışırsınız. Etiket yerine boş bir dize döndürmek de olur, ama etiket hem müşteri hem mağaza için daha iyidir: “Price on request” size ulaşması gerektiğini söyler, boş bir aralık ise sitenin bozuk olduğunu.

Kapsama dikkat edin. Bu filtre katalogdaki fiyat gösterimini yönetir. Sepet, mini sepet, ödeme sayfası ve sipariş e-postaları satır kalemlerini tamamen başka filtrelerle biçimlendirir; woocommerce_cart_item_price bunlardan biri. is_purchasable false döndürdüğünde sepete zaten hiçbir şey giremez, dolayısıyla bu yüzeyler konu dışı kalır. Fiyat gizleme yarısını satın almayı kapatma yarısı olmadan çalıştırıyorsanız durum değişir; o zaman onları ayrıca ele alın.

Varyasyonlu ürünlere ikinci kez bakmak gerekir. Ana ürünün aralığı yukarıdaki filtreden geçer; varyasyon JSON’ı içindeki price_html dizesi de geçer, çünkü WC_Product_Variable::get_available_variation() o dizeyi get_price_html() çağırarak kurar. Filtreden geçmeyen şey, aynı bloğun içindeki ham sayılardır: display_price ve display_regular_price birer float’tır ve formun data-product_variations özniteliğine basılır. Bu teorik değil, gerçek bir sızıntı: varyasyonlu bir üründe kaynağı görüntüleyin, display_price araması yapın; sayılarınız düz metin olarak orada duruyor. Çözüm woocommerce_available_variation filtresi; ayrıntılar varyasyonlu ürünlerde fiyat aralığını gizleme yazısında.

Adım 4: hazır buradayken değiştirmeniz gereken ayarlar#

Snippet’lerinizin dışında kalan iki şey, dokunmadan bırakırsanız yaptığınız işi geri alır.

  • Sepet ve Ödeme sayfaları. Kimse hiçbir şey ekleyemediği için sayfalar zararsız; ayrıca WooCommerce sepet, ödeme ve Hesabım sayfalarında zaten bir noindex yönergesi basıyor. Yine de menülerde ve site haritası eklentinizin ürettiği çıktıda duruyorlar, oraları toparlayın.
  • Ürün akışları ve pazarlama entegrasyonları. CTX Feed, bir Google Merchant entegrasyonu, Facebook for WooCommerce ya da benzeri bir şey çalıştırıyorsanız, bunlar ham fiyatı doğrudan veritabanından okur ve gösterim filtrelerinize hiç uğramaz. Temadaki katalog modu onlara hiçbir şey yapmaz. Onları bilerek kapatın.

Yayına aldıktan sonra sayfa önbelleğinizi de temizleyin. İçinde fiyat varken önbelleğe alınmış bir sayfa, içinde fiyatla önbellekte kalır; siz de yirmi dakikanızı filtrenizin bozuk olduğuna inanarak geçirirsiniz.

Eklentisiz WooCommerce katalog modu nerelerden sızdırır#

Çoğu rehberin atladığı kısım burası. Yukarıdaki hook’lar klasik PHP render yolunu kapsar. Başka hiçbir şeyi kapsamaz ve modern WooCommerce’te “başka şey” bir hayli fazla.

Blok temalar sizin filtrelerinizden değil, Store API’den render eder#

Temanız bir blok temaysa, mağaza sayfanız Products bloğunu kullanıyorsa ya da sepet ve ödeme sayfalarınız blok sürümleriyse, fiyatı ve düğmeyi woocommerce_template_loop_add_to_cart hiç basmıyordur. Bunlar JavaScript tarafından /wp-json/wc/store/v1/products adresindeki Store API’den çekilir; API her ürünü kendi şeması üzerinden serileştirir ve hem bir price_html dizesi hem de yapılandırılmış bir prices nesnesi döndürür.

Şema price_html alanını get_price_html() üzerinden kurar, yani filtreniz o alana ulaşır. Sorun prices nesnesi: normal fiyat, indirimli fiyat, varyasyonlu bir ürün için en düşük ve en yüksek değer; hepsi alt birim cinsinden, doğrudan ürün verisinden alınmış ve yakınlarında hiçbir gösterim filtresi yok. Katalog modundaki kendi mağazanızda bu adresi bir tarayıcıda açın ve JSON’ı okuyun. Kaldırdığınızı sandığınız her sayı orada.

Koleksiyon uç noktası daha kötüsü, çünkü toplulaştırıyor: /wp-json/wc/store/v1/products/collection-data adresine fiyat aralığı isteğiyle gidildiğinde bütün bir kategorideki en düşük ve en yüksek fiyat döner; bir rakibin istediği sayı tam olarak budur.

Store API yalnızca bir render uç noktası değil, aynı zamanda bir sepet uç noktası#

Yalnızca düğmeyi kaldırmak yerine woocommerce_is_purchasable kullanmanın nedeni bu. /wp-json/wc/store/v1/cart/add-item adresine yapılan bir POST hiçbir şablon render etmez, dolayısıyla şablon aksiyonlarını kaldırmanın bu isteğe hiçbir etkisi yoktur. Satın alınabilirliği ise denetler, yani ilk snippet iş görür. Ama onu atlayıp yalnızca kozmetik şablon kaldırmasını yaptıysanız, “katalog modundaki” mağazanız API belgelerini okuyan herkesten sipariş alır. Bunu test edin. Düğme kaldırmak bir güvenlik sınırı değildir.

Satın alınamaz olmak varyasyon formunun kaybolduğu anlamına gelmez#

Varyasyonlu bir üründe woocommerce_template_single_add_to_cart(), woocommerce_variable_add_to_cart aksiyonunu tetikler; o da varyasyon formunun tamamını basar: nitelik açılır listeleri, sıfırlama bağlantısı, tekil varyasyon kutusu ve az önce anlatılan JSON bloğu. Bu şablon kendini satın alınabilirliğe göre kapatmaz. Varyasyonları satın alınamaz yapmak eklemenin başarılı olmasını engeller, ama elinizde bir varyasyon seçtikten sonra hiçbir şey göstermeyen ya da gizlemek istediğiniz fiyatı gösteren açılır listeler kalabilir. Aksiyonu 30 önceliğinde kaldırmak bu işi çözer; yalnızca satın alınabilirliğe güvenmek çözmez.

Yapılandırılmış veri, REST API ve GraphQL#

WooCommerce ürün JSON-LD’sini oluşturup wp_footer üzerinde sayfaya basar; içindeki offers.price alanı sizin fiyat HTML filtrenizden değil, ürün nesnesinden alınır. Kendi ürün şemasını üreten SEO eklentileri, yani Rank Math, Yoast ve All in One SEO, aynı şeyi kendi çıktılarında bir kez daha yapar. wc/v3 REST API’si, okuma anahtarı elinde olan herkese price, regular_price ve sale_price döndürür. WooGraphQL ile birlikte WPGraphQL de aynı alanları kabul ettiği her sorguya açar.

Bunların hepsini filtreleyebilirsiniz. Birkaç düzine satır daha ve WooCommerce ile SEO eklentileri değiştikçe güncel tutmanız gereken bir hook listesi demektir. Sihir değil, dürüst bir iş. Ama iş, ve küçük bir sürüm yükseltmesinde sessizce bozulan kısım da tam olarak burası.

Snippet’lerin kapsadığı ve kapsamadığı yüzeyler#

YüzeyYukarıdaki snippet’ler kapsıyor mu
Klasik tekil ürün sayfasıEvet
Klasik mağaza döngüsü, ilgili ürünler, üst satışEvet
Form POST ile sepete eklemeEvet (is_purchasable)
Varyasyonlu ürünün fiyat aralığıEvet
Satır içi varyasyon JSON’ı (display_price)Hayır — kendi filtresi gerekir
Store API price_htmlEvet
Store API prices nesnesiHayır
Store API koleksiyon fiyat aralığıHayır
Store API sepete yazmais_purchasable ile reddedilir
JSON-LD yapılandırılmış veriHayır
SEO eklentisi şemasıHayır
wc/v3 REST APIHayır
WPGraphQL / WooGraphQLHayır
Ürün akışı eklentileriHayır — kendiniz kapatın
get_price() fonksiyonunu doğrudan çağıran tema koduHayır — yalnızca o kodu düzenlemek çözer

Bu tabloyu korku listesi olarak değil, karar aracı olarak okuyun. Klasik bir tema kullanıyorsanız, SEO şemanız yoksa, headless bir ön yüzünüz yoksa ve açık API anahtarınız yoksa, sol sütun bütün mağazanız demektir ve eklenti yardımı olmadan WooCommerce katalog modu gerçekten eksiksizdir. Pek çok mağaza tam olarak bu konumda. Kodu yayına alın ve yolunuza devam edin.

“Hayır” satırları sizin sitenizi tarif ediyorsa seçim şu: bu filtreleri kendiniz yazıp bakımını üstlenmek ya da bunu hâlihazırda yapan bir şey kullanmak. PriceVeil aynı listeyi sunucu tarafında kapsıyor: mağaza vitrini, varyasyon JSON’ı, Store API ürün ve koleksiyon yanıtları, çekirdeğin ve SEO eklentilerinin yapılandırılmış verisi, wc/v3 REST API’si ve WooGraphQL; hepsi ücretsiz sürümde. Katalog modu da blok düğmesini gizlemekle yetinmiyor, Store API sepet yazmalarını reddediyor. Tablonun son iki satırında yardımcı olamıyor ve belgeler bunu açıkça söylüyor: akış eklentileri ve tema kodundaki doğrudan get_price() çağrıları hâlâ sizin işiniz.

İnanmadan önce test edin#

Hangi yolu seçerseniz seçin, varsaymak yerine doğrulayın. Oturumunuz kapalı olsun diye gizli bir pencerede, beş dakikalık bir kontrol:

  • Bir ürün sayfasında kaynağı görüntüleyin. Bildiğiniz bir fiyat dizesini arayın, sonra display_price için ve belgenin sonuna yakın duran JSON-LD bloğunun içinde "price" için yeniden arayın.
  • Store API’yi doğrudan açın. /wp-json/wc/store/v1/products?per_page=5 adresine gidin ve prices nesnelerini okuyun.
  • Koleksiyon uç noktasını deneyin. Fiyat aralığı verisi isteyin ve en düşük ile en yüksek değerlerin hayatta kalıp kalmadığına bakın.
  • Sepete yazma denemesi yapın. /wp-json/wc/store/v1/cart/add-item adresine bir ürün ID’siyle POST gönderin ve isteğin reddedildiğini doğrulayın.
  • Varyasyonlu bir ürünü kontrol edin. Her nitelik bileşimini seçin ve fiyatın görünmesi gereken yerde ne belirdiğini izleyin.

Bu, kendi sürümlerimiz için otomatikleştirdiğimiz prosedürün aynısı; ayrıntısı her WooCommerce eklentisini nasıl sızıntı testinden geçiriyoruz yazısında. Sonunda PriceVeil kurarsanız aynı tur tek komuta iniyor: wp priceveil selftest her ürünün fiyat HTML’ini, yapılandırılmış verisini, Store API yanıtını ve koleksiyon fiyat aralığını denetliyor, ilk sızıntıda sıfırdan farklı bir kodla çıkıyor. Elle yapmak da aynı işi görür; önemli olan, her tema ya da WooCommerce güncellemesinden sonra testi yeniden çalıştırmanız, çünkü buradaki hata biçimi sessiz. Hiçbir şey hata vermez. Bir fiyat öylece geri gelir.

Bundan sonra ne yapmalı#

Snippet’leri functions.php yerine siteye özel bir eklentiye koyun; böylece bir tema değişikliği katalog modunuzu da beraberinde götürmesin. Önbelleği temizleyin. Yukarıdaki beş kontrolü oturumu kapalı bir tarayıcıda çalıştırın ve hangi yüzeylerin hâlâ sayı gösterdiğini not edin.

Sonra gizlenen fiyatın müşteriyi nereye götüreceğine karar verin. Sonraki adımı olmayan bir katalog modu müşteri için çıkmaz sokaktır: “Price on request” yazısını görür ve tıklayacak bir yer bulamaz. Bunu doğru yapan mağazaların çoğu katalog modunu ya bir giriş duvarıyla eşliyor, ki bu müşteriler giriş yapana kadar fiyatları gizleme yazısında ele alınıyor, ya da bir teklif formuyla eşliyor, o da teklif isteme akışı ekleme yazısında. Gizlemek işin kolay yarısı; katalogu satış kanalına çeviren şey, sormak.

Okumaya devam

İlgili yazılar