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ă.

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.
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!