Cum să adaugi opțiuni de gramaj și aromă pentru produse în WooCommerce

YouTube Logo Urmărește canalul nostru de YouTube

Dacă ai un magazin WooCommerce și vrei să vinzi produse cu gramaje și arome diferite, ai nevoie de o soluție personalizată care să le permită clienților să aleagă exact varianta dorită. În acest articol, îți voi arăta pas cu pas cum să adaugi aceste opțiuni în admin, să le salvezi în baza de date și să le afișezi pe pagina produsului, în coș, în comandă și în emailurile trimise către client.

De ce ai nevoie de această funcționalitate?

În WooCommerce, ai posibilitatea de a crea variații, dar uneori vrei o soluție mai simplă, care să nu necesite multiple produse derivate. De exemplu, dacă vinzi gemuri cu mai multe gramaje și arome, ai nevoie de un selector ușor de utilizat care să adauge automat informațiile în comandă.

Imagine articol

 

Prin acest sistem:

  • Clienții pot alege gramajul și aroma preferată.
  • Prețul se actualizează automat în funcție de gramaj.
  • Se adaugă informațiile în coș și comandă.
  • Se trimit detaliile în emailurile de confirmare.

YouTube Logo
Vezi tutorialul pe YouTube

Codul complet il gasesti aici :

<?php

// 1. Adăugare câmpuri în admin pentru gramaje și arome
add_action('woocommerce_product_options_general_product_data', 'micadan_add_gem_options');
function micadan_add_gem_options() {
    global $product_object;
    
    // Checkbox produs gem
    woocommerce_wp_checkbox(array(
        'id' => 'is_gem_product',
        'label' => '🔮 Produs de tip Gem',
        'description' => 'Activează pentru a permite selecția de gramaje și arome',
        'value' => $product_object->get_meta('is_gem_product', true)
    ));

    // Grup de opțiuni condițional (ascuns inițial)
    echo '<div class="options_group micadan-gem-options" style="display: none;">';

    // Gramaje și prețuri
    echo '<p class="form-field"><strong>🎚️ Gramaje disponibile</strong></p>';
    $sizes = ['150g' => 'Gramaj 150g', '250g' => 'Gramaj 250g', '500g' => 'Gramaj 500g'];
    foreach ($sizes as $size => $label) {
        woocommerce_wp_checkbox(array(
            'id' => 'gem_size_' . sanitize_title($size),
            'label' => $label,
            'value' => $product_object->get_meta('gem_size_' . sanitize_title($size), true)
        ));
        woocommerce_wp_text_input(array(
            'id' => 'gem_price_' . sanitize_title($size),
            'label' => 'Preț ' . $size,
            'type' => 'number',
            'custom_attributes' => array('step' => '0.01'),
            'value' => $product_object->get_meta('gem_price_' . sanitize_title($size), true)
        ));
    }

    // Arome
    echo '<p class="form-field"><strong>🍭 Arome disponibile</strong></p>';
    for ($i = 1; $i <= 3; $i++) {
        woocommerce_wp_text_input(array(
            'id' => 'gem_flavor_' . $i,
            'label' => 'Aromă ' . $i,
            'value' => $product_object->get_meta('gem_flavor_' . $i, true)
        ));
    }

    echo '</div>';

    // Script pentru a afișa/ascunde opțiunile în funcție de checkbox
    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function() {
        let checkbox = document.querySelector('#is_gem_product');
        let optionsGroup = document.querySelector('.micadan-gem-options');

        function toggleGemOptions() {
            if (checkbox.checked) {
                optionsGroup.style.display = 'block';
            } else {
                optionsGroup.style.display = 'none';
            }
        }

        // Inițial verificăm starea checkbox-ului
        toggleGemOptions();

        // Adăugăm eveniment pentru a afișa/ascunde când se schimbă checkbox-ul
        checkbox.addEventListener('change', toggleGemOptions);
    });
    </script>
    <?php
} 

// 2. Salvarea datelor în admin
add_action('woocommerce_admin_process_product_object', 'micadan_save_gem_options');
function micadan_save_gem_options($product) {
    $product->update_meta_data('is_gem_product', isset($_POST['is_gem_product']) ? 'yes' : 'no');

    $sizes = ['150g', '250g', '500g'];
    foreach ($sizes as $size) {
        $size_key = sanitize_title($size);
        $product->update_meta_data('gem_size_' . $size_key, isset($_POST['gem_size_' . $size_key]) ? 'yes' : 'no');
        $product->update_meta_data('gem_price_' . $size_key, wc_clean($_POST['gem_price_' . $size_key]));
    }

    for ($i = 1; $i <= 3; $i++) {
        $product->update_meta_data('gem_flavor_' . $i, wc_clean($_POST['gem_flavor_' . $i]));
    }
}

// 3. Salvarea gramajului și aromei în comandă
add_action('woocommerce_checkout_create_order_line_item', 'micadan_save_order_item_meta', 10, 4);
function micadan_save_order_item_meta($item, $cart_item_key, $values, $order) {
    if (!empty($values['gem_data'])) {
        $item->add_meta_data('Gramaj', $values['gem_data']['size'], true);
        $item->add_meta_data('Aromă', $values['gem_data']['flavor'], true);
    }
}

// 3.1. Afișare interfață de selecție pe frontend
add_action('woocommerce_before_add_to_cart_button', 'micadan_display_gem_buttons');
function micadan_display_gem_buttons() {
    global $product;

    if ($product->get_meta('is_gem_product') !== 'yes') return;

    // Colectare gramaje disponibile
    $available_sizes = [];
    $sizes = ['150g', '250g', '500g'];
    foreach ($sizes as $size) {
        $size_key = sanitize_title($size);
        if ($product->get_meta('gem_size_' . $size_key) === 'yes') {
            $price = (float)$product->get_meta('gem_price_' . $size_key);
            if ($price > 0) {
                $available_sizes[$size] = $price;
            }
        }
    }

    // Colectare arome
    $flavors = [];
    for ($i = 1; $i <= 3; $i++) {
        $flavor = $product->get_meta('gem_flavor_' . $i);
        if (!empty($flavor)) {
            $flavors[] = $flavor;
        }
    }

    // Afișare interfață
    ?>
    <div class="micadan-gem-selector" style="margin: 20px 0;">

        <!-- Gramaje -->
        <div class="gem-size-selector">
            <h4>Alege gramajul:</h4> 
            <div class="gem-buttons">
                <?php foreach ($available_sizes as $size => $price): ?>
                <button type="button" 
                        class="gem-btn" 
                        data-size="<?php echo esc_attr($size); ?>"
                        data-price="<?php echo esc_attr($price); ?>"
                        style="display: flex; align-items: center;">
                    <img src="https://tutoriale.site/wp-content/uploads/2025/02/honey-2.png" alt="Borcan" style="width: 20px; height: 20px; margin-right: 5px;">
                    <?php echo esc_html($size); ?> 
                </button>
                <?php endforeach; ?>
            </div>
        </div>

        <!-- Arome -->
        <div class="gem-flavor-selector" style="margin-top: 15px;">
            <h4>Alege aroma:</h4>
            <div class="gem-buttons">
                <?php foreach ($flavors as $flavor): ?>
                <button type="button" 
                        class="gem-btn" 
                        data-flavor="<?php echo esc_attr($flavor); ?>"
                        style="display: flex; align-items: center;">
                    <img src="https://tutoriale.site/wp-content/uploads/2025/02/fruit.png" alt="Aroma" style="width: 20px; height: 20px; margin-right: 5px;">
                    <?php echo esc_html($flavor); ?>
                </button>
                <?php endforeach; ?>
            </div>
        </div>

        <!-- Câmpuri ascunse pentru coș -->
        <input type="hidden" name="gem_selected_size" value="">
        <input type="hidden" name="gem_selected_flavor" value="">
        <input type="hidden" name="gem_custom_price" value="">

        <!-- Validare client-side -->
        <div class="gem-errors" style="color: #ff0000; margin-top: 10px; display: none;"></div>
    </div>

    <!-- Stiluri și script -->
    <style>
    .gem-buttons { display: flex; gap: 10px; flex-wrap: wrap; }
    .gem-btn {
        padding: 12px 20px;
        border: 2px solid #ddd;
        background: #f8f8f8;
        cursor: pointer;
        border-radius: 8px;
        transition: all 0.3s;
    }
    .gem-btn.active {
        background: #ffcc00 !important;
        border-color: #ffcc00;
        transform: scale(1.05);
    }
    </style>

    <script>
    document.addEventListener('DOMContentLoaded', function() {
    const gemSelector = document.querySelector('.micadan-gem-selector');
    let selectedSize = null;
    let selectedFlavor = null;

    // Evenimente pentru selecție
    gemSelector.querySelectorAll('.gem-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            // Toggle active class
            this.classList.toggle('active');
            
            if (this.closest('.gem-size-selector')) {
                selectedSize = this.classList.contains('active') ? this : null;
                gemSelector.querySelectorAll('.gem-size-selector .gem-btn').forEach(b => {
                    if (b !== this) b.classList.remove('active');
                });
            } else {
                selectedFlavor = this.classList.contains('active') ? this : null;
                gemSelector.querySelectorAll('.gem-flavor-selector .gem-btn').forEach(b => {
                    if (b !== this) b.classList.remove('active');
                });
            }

            updateSelections();
        });
    });

    // Actualizează câmpurile ascunse
    function updateSelections() {
        const errors = gemSelector.querySelector('.gem-errors');
        
        if (selectedSize && selectedFlavor) {
            errors.style.display = 'none';
            
            // Actualizează prețul
            const price = parseFloat(selectedSize.dataset.price);
            document.querySelector('input[name="gem_custom_price"]').value = price.toFixed(2);
            
            // Actualizează câmpurile
            document.querySelector('input[name="gem_selected_size"]').value = selectedSize.dataset.size;
            document.querySelector('input[name="gem_selected_flavor"]').value = selectedFlavor.dataset.flavor;
            
            // Actualizează afișajul prețului cu moneda și păstrează eticheta "Preț:"
            const priceWrapper = document.querySelector('.price');
            if (priceWrapper) {
                const currencySymbol = priceWrapper.querySelector('.woocommerce-Price-currencySymbol')?.innerHTML || 'RON';

                // Verifică dacă elementul "custom-price-label" există, dacă nu, îl adaugă
                let priceLabel = priceWrapper.querySelector('.custom-price-label');
                if (!priceLabel) {
                    priceLabel = document.createElement('span');
                    priceLabel.classList.add('custom-price-label');
                    priceLabel.textContent = 'Preț: ';
                    priceWrapper.prepend(priceLabel);
                }

                priceWrapper.innerHTML = `
                    <span class="custom-price-label">Preț: </span>
                    <span class="woocommerce-Price-amount amount">
                        <bdi>${price.toFixed(2)} <span class="woocommerce-Price-currencySymbol">${currencySymbol}</span></bdi>
                    </span>
                `;
            }
        } else {
            document.querySelector('input[name="gem_custom_price"]').value = '';
            errors.textContent = '⚠️ Te rugăm să selectezi atât gramajul cât și aroma!';
            errors.style.display = 'block';
        }
    }
});

    </script>
    <?php
}

// 4. Gestionare coș și prețuri
add_filter('woocommerce_add_cart_item_data', 'micadan_add_cart_item_data', 10, 3);
function micadan_add_cart_item_data($cart_item_data, $product_id, $variation_id) {
    if (!empty($_POST['gem_custom_price'])) {
        $cart_item_data['gem_data'] = array(
            'size' => sanitize_text_field($_POST['gem_selected_size']),
            'flavor' => sanitize_text_field($_POST['gem_selected_flavor']),
            'price' => (float)$_POST['gem_custom_price']
        );
        $cart_item_data['unique_key'] = md5($product_id . serialize($cart_item_data['gem_data']));
    }
    return $cart_item_data;
}

add_action('woocommerce_before_calculate_totals', 'micadan_update_cart_prices', 20, 1);
function micadan_update_cart_prices($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    foreach ($cart->get_cart() as $cart_item) {
        if (!empty($cart_item['gem_data']['price'])) {
            $cart_item['data']->set_price($cart_item['gem_data']['price']);
        }
    }
}

// 5. Afișare detalii în coș
add_filter('woocommerce_get_item_data', 'micadan_display_cart_item_data', 10, 2);
function micadan_display_cart_item_data($item_data, $cart_item) {
    if (!empty($cart_item['gem_data'])) {
        $item_data[] = array(
            'name' => 'Gramaj',
            'value' => $cart_item['gem_data']['size']
        );
        $item_data[] = array(
            'name' => 'Aromă',
            'value' => $cart_item['gem_data']['flavor']
        );
    }
    return $item_data;
}

Pasul 1: Adăugarea Opțiunilor în Admin

Pentru a permite administratorului să seteze gramajele și aromele disponibile pentru fiecare produs, trebuie să adăugăm câmpuri personalizate în pagină.

Ce face acest cod?

  • Adaugă un checkbox pentru a activa opțiunea de selecție.
  • Oferă o interfață unde se pot adăuga gramajele și aromele disponibile.
  • Salvează datele în baza de date WooCommerce.

💡 Notă: Dacă checkbox-ul nu este activat, câmpurile suplimentare nu vor apărea.

Pasul 2: Afișarea Opțiunilor pe Pagina Produsului

După ce ai configurat opțiunile în admin, trebuie să le afișăm pe frontend, astfel încât clientul să poată selecta varianta dorită. Vom folosi butoane pentru o experiență de utilizare mai bună.

Ce face acest cod?

  • Afișează gramajele disponibile ca butoane.
  • Afișează aromele ca butoane.
  • Actualizează prețul în funcție de selecție.
  • Trimite datele selectate la coșul de cumpărături.

Pasul 3: Salvarea Selecției în Comandă

După ce clientul adaugă produsul în coș, trebuie să ne asigurăm că informațiile despre gramaj și aromă sunt salvate corect. Acest pas este esențial pentru a evita confuziile la procesarea comenzii.

Ce face acest cod?

  • Salvează gramajul și aroma în datele produsului din coș.
  • Se asigură că prețul se actualizează corect în funcție de selecție.
  • Stochează datele în baza de date WooCommerce, astfel încât administratorul să le poată vedea în admin.

Pasul 4: Afișarea Detaliilor în Comandă 

Ultimul pas este să ne asigurăm că informațiile despre gramaj și aromă apar atât în administrarea comenzii.

Ce face acest cod?

  • Afișează selecțiile în rezumatul comenzii din admin.
  • Oferă o vizibilitate clară asupra variantelor alese.

Acum că ai o idee clară despre cum funcționează această funcționalitate, poți implementa codul și adapta în funcție de nevoile tale.

📌 Dacă ai întrebări sau vrei să îmbunătățești această soluție, lasă un comentariu!

× YouTube Logo

🔔 Abonează-te la canalul nostru!

Primește cele mai noi tutoriale WordPress direct pe YouTube!

×