Skip to content

Instantly share code, notes, and snippets.

@kartikparmar
Created November 13, 2025 07:16
Show Gist options
  • Save kartikparmar/0c56fb04f80e6a2582a771df7755f814 to your computer and use it in GitHub Desktop.
Save kartikparmar/0c56fb04f80e6a2582a771df7755f814 to your computer and use it in GitHub Desktop.
Custom Field On Product page and edit button on the Cart/Checkout page : Compatible with Cart/Checkout Blocks
<?php
/**
* WooCommerce Custom Item Meta Data with Edit Functionality
* Compatible with both classic and block-based cart/checkout
*/
// 1. Add custom field on product page
add_action('woocommerce_before_add_to_cart_button', 'add_custom_name_field');
function add_custom_name_field() {
?>
<div class="custom-name-field" style="margin-bottom: 20px;">
<label for="custom_name"><?php _e('Enter Your Name:', 'woocommerce'); ?></label>
<input type="text" id="custom_name" name="custom_name" value="" placeholder="Enter name" style="width: 100%; padding: 8px; margin-top: 5px;">
</div>
<?php
}
// 2. Add custom field data to cart item
add_filter('woocommerce_add_cart_item_data', 'add_custom_name_to_cart_item', 10, 3);
function add_custom_name_to_cart_item($cart_item_data, $product_id, $variation_id) {
if (isset($_POST['custom_name']) && !empty($_POST['custom_name'])) {
$cart_item_data['custom_name'] = sanitize_text_field($_POST['custom_name']);
}
return $cart_item_data;
}
// 3. Display custom field in cart (Classic Cart)
add_filter('woocommerce_get_item_data', 'display_custom_name_in_cart', 10, 2);
function display_custom_name_in_cart($item_data, $cart_item) {
if (isset($cart_item['custom_name'])) {
$item_data[] = array(
'key' => __('Custom Name', 'woocommerce'),
'value' => wc_clean($cart_item['custom_name']),
'display' => ''
);
}
return $item_data;
}
// 4. Display custom field in cart blocks (Block-based Cart)
add_filter('woocommerce_store_api_product_quantity_editable', '__return_true');
add_action('woocommerce_store_api_cart_update_item_before', 'handle_cart_item_meta_update', 10, 2);
function handle_cart_item_meta_update($cart_item, $request) {
if (isset($request['custom_name'])) {
$cart_item['custom_name'] = sanitize_text_field($request['custom_name']);
}
}
// 5. Save custom field to order items
add_action('woocommerce_checkout_create_order_line_item', 'save_custom_name_to_order', 10, 4);
function save_custom_name_to_order($item, $cart_item_key, $values, $order) {
if (isset($values['custom_name'])) {
$item->add_meta_data(__('Custom Name', 'woocommerce'), $values['custom_name']);
}
}
// 6. Add edit button in cart (Classic Cart)
add_action('woocommerce_after_cart_item_name', 'add_edit_button_in_cart', 10, 2);
function add_edit_button_in_cart($cart_item, $cart_item_key) {
if (isset($cart_item['custom_name'])) {
?>
<button type="button" class="edit-custom-name-btn"
data-cart-key="<?php echo esc_attr($cart_item_key); ?>"
data-current-name="<?php echo esc_attr($cart_item['custom_name']); ?>"
style="font-size: 12px; padding: 4px 8px; margin-left: 10px; cursor: pointer;">
<?php _e('Edit Name', 'woocommerce'); ?>
</button>
<?php
}
}
// 7. Add edit button in checkout (Classic Checkout)
add_action('woocommerce_checkout_after_order_review', 'add_edit_button_in_checkout');
function add_edit_button_in_checkout() {
$cart = WC()->cart->get_cart();
foreach ($cart as $cart_item_key => $cart_item) {
if (isset($cart_item['custom_name'])) {
// Buttons are already added in review order table
}
}
}
// 8. Add popup modal HTML
add_action('wp_footer', 'add_custom_name_edit_modal');
function add_custom_name_edit_modal() {
if (is_cart() || is_checkout()) {
?>
<div id="edit-name-modal" style="display:none; position:fixed; z-index:9999; left:0; top:0; width:100%; height:100%; background-color:rgba(0,0,0,0.5);">
<div style="background-color:#fff; margin:10% auto; padding:30px; width:90%; max-width:400px; border-radius:8px; position:relative;">
<span class="close-modal" style="position:absolute; right:15px; top:10px; font-size:28px; font-weight:bold; cursor:pointer;">&times;</span>
<h3><?php _e('Edit Custom Name', 'woocommerce'); ?></h3>
<div id="edit-name-form">
<input type="hidden" id="edit-cart-key" name="cart_key" value="">
<label for="edit-custom-name"><?php _e('Name:', 'woocommerce'); ?></label>
<input type="text" id="edit-custom-name" name="custom_name" style="width:100%; padding:10px; margin:10px 0; border:1px solid #ddd; border-radius:4px;">
<div id="update-message" style="display:none; padding:10px; margin:10px 0; border-radius:4px; text-align:center;"></div>
<button type="button" id="update-name-btn" style="width:100%; padding:12px; background-color:#0071a1; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:16px;">
<?php _e('Update Name', 'woocommerce'); ?>
</button>
</div>
</div>
</div>
<?php
}
}
// 9. Enqueue JavaScript for modal and AJAX
add_action('wp_enqueue_scripts', 'enqueue_custom_name_scripts');
function enqueue_custom_name_scripts() {
if (is_cart() || is_checkout()) {
// wp_enqueue_script('custom-name-edit', get_template_directory_uri() . '/js/custom-name-edit.js', array('jquery'), '1.0', true);
// Inline script if you don't want separate file
wp_add_inline_script('jquery', "
jQuery(document).ready(function($) {
// Open modal
$(document).on('click', '.edit-custom-name-btn', function() {
var cartKey = $(this).data('cart-key');
var currentName = $(this).data('current-name');
$('#edit-cart-key').val(cartKey);
$('#edit-custom-name').val(currentName);
$('#update-message').hide();
$('#edit-name-modal').fadeIn();
$('#edit-name-form').removeData('is-block').removeData('cart-index');
});
// Close modal
$('.close-modal, #edit-name-modal').on('click', function(e) {
if (e.target === this) {
$('#edit-name-modal').fadeOut();
}
});
// Handle button click
$(document).on('click', '#update-name-btn', function(e) {
e.preventDefault();
var button = $(this);
var cartKey = $('#edit-cart-key').val();
var newName = $('#edit-custom-name').val();
var isBlock = $('#edit-name-form').data('is-block') || false;
var cartIndex = $('#edit-name-form').data('cart-index') || null;
// Disable button and show loading
button.prop('disabled', true).text('Updating...');
$('#update-message').hide();
$.ajax({
url: wc_cart_params.ajax_url,
type: 'POST',
data: {
action: 'update_cart_item_name',
cart_key: cartKey,
custom_name: newName,
is_block: isBlock,
cart_index: cartIndex,
security: '" . wp_create_nonce('update-cart-item-name') . "'
},
success: function(response) {
if (response.success) {
// Show success message
$('#update-message')
.removeClass('error')
.addClass('success')
.css({'background-color': '#d4edda', 'color': '#155724', 'border': '1px solid #c3e6cb'})
.text('Name updated successfully!')
.fadeIn();
// For block-based cart/checkout - trigger proper cart refresh
if (isBlock && typeof wp !== 'undefined' && wp.data) {
// Invalidate cart cache to force re-fetch from API
wp.data.dispatch('wc/store/cart').invalidateResolutionForStore();
// Also trigger receiveCart to refresh immediately
setTimeout(function() {
if (wp.data.select('wc/store/cart').getCartData) {
// This will trigger the /wp-json/wc/store/v1/batch call
wp.data.dispatch('wc/store/cart').receiveCart(
wp.data.select('wc/store/cart').getCartData()
);
}
}, 100);
}
// Close modal after short delay
setTimeout(function() {
$('#edit-name-modal').fadeOut();
button.prop('disabled', false).text('Update Name');
// For classic cart - update the page manually or trigger mini-cart refresh
if (!isBlock) {
// Update displayed value
$('[data-cart-key=\"' + cartKey + '\"]').data('current-name', newName);
$('[data-cart-key=\"' + cartKey + '\"]').closest('tr, .cart_item').find('.wc-item-meta li').each(function() {
if ($(this).text().includes('Custom Name')) {
$(this).find('p').text(newName);
}
});
// Trigger WooCommerce cart updated event
$(document.body).trigger('wc_update_cart');
$(document.body).trigger('updated_wc_div');
}
}, 800);
} else {
// Show error message
$('#update-message')
.removeClass('success')
.addClass('error')
.css({'background-color': '#f8d7da', 'color': '#721c24', 'border': '1px solid #f5c6cb'})
.text(response.data.message || 'Error updating name')
.fadeIn();
button.prop('disabled', false).text('Update Name');
}
},
error: function() {
$('#update-message')
.removeClass('success')
.addClass('error')
.css({'background-color': '#f8d7da', 'color': '#721c24', 'border': '1px solid #f5c6cb'})
.text('Error updating name. Please try again.')
.fadeIn();
button.prop('disabled', false).text('Update Name');
}
});
});
});
");
}
}
// 10. AJAX handler to update cart item meta
add_action('wp_ajax_update_cart_item_name', 'update_cart_item_name_ajax');
add_action('wp_ajax_nopriv_update_cart_item_name', 'update_cart_item_name_ajax');
function update_cart_item_name_ajax() {
check_ajax_referer('update-cart-item-name', 'security');
$cart_item_key = sanitize_text_field($_POST['cart_key']);
$new_name = sanitize_text_field($_POST['custom_name']);
$cart = WC()->cart->get_cart();
if (isset($cart[$cart_item_key])) {
$cart[$cart_item_key]['custom_name'] = $new_name;
WC()->cart->set_cart_contents($cart);
WC()->cart->calculate_totals();
wp_send_json_success(array('message' => __('Name updated successfully', 'woocommerce')));
} else {
wp_send_json_error(array('message' => __('Cart item not found', 'woocommerce')));
}
}
// 11. For Block-based Cart/Checkout - Extend cart item schema
add_filter('woocommerce_store_api_product_quantity_limit', '__return_true');
// Expose custom meta to Store API
add_filter('woocommerce_store_api_cart_item_data', 'expose_custom_name_to_api', 10, 2);
function expose_custom_name_to_api($item_data, $cart_item) {
if (isset($cart_item['custom_name'])) {
$item_data['custom_name'] = $cart_item['custom_name'];
}
return $item_data;
}
// 12. Add edit button for Block-based Cart/Checkout
add_action('wp_footer', 'enqueue_block_cart_custom_scripts');
function enqueue_block_cart_custom_scripts() {
if (has_block('woocommerce/cart') || has_block('woocommerce/checkout')) {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Function to add edit buttons to block cart items
function addEditButtonsToBlocks() {
// Get all cart items first
var allCartItems = $('.wc-block-cart-item, tr.wc-block-cart-items__row');
// Target the exact structure from your HTML
$('.wc-block-components-product-details__custom-name').each(function(index) {
var detailRow = $(this);
var nameLabel = detailRow.find('.wc-block-components-product-details__name').text().trim();
if (nameLabel.toLowerCase().includes('custom name')) {
var valueElement = detailRow.find('.wc-block-components-product-details__value');
var metaValue = valueElement.text().trim();
// Check if button already exists
if (detailRow.find('.edit-custom-name-btn-block').length === 0) {
// Find the cart item - try multiple parent selectors
var cartItem = detailRow.closest('.wc-block-cart-item');
if (cartItem.length === 0) {
cartItem = detailRow.closest('tr.wc-block-cart-items__row');
}
if (cartItem.length === 0) {
cartItem = detailRow.closest('td.wc-block-cart-item__product').parent();
}
var cartItemIndex = -1;
if (cartItem.length > 0) {
cartItemIndex = allCartItems.index(cartItem);
}
// Fallback: use the index of this detail row itself
if (cartItemIndex === -1) {
cartItemIndex = index;
}
// Add edit button
var editButton = $('<button/>', {
'type': 'button',
'class': 'edit-custom-name-btn edit-custom-name-btn-block',
'data-current-name': metaValue,
'data-cart-index': cartItemIndex,
'html': '✏️ Edit Name',
'css': {
'font-size': '12px',
'padding': '5px 10px',
'margin-left': '10px',
'cursor': 'pointer',
'background': '#0071a1',
'color': '#fff',
'border': 'none',
'border-radius': '4px',
'display': 'inline-block',
'vertical-align': 'middle',
'font-weight': '500'
}
});
// Append button to the detail row (not inside value span)
detailRow.append(' ').append(editButton);
}
}
});
// Also check for dt/dd format (for other WC versions)
$('.wc-block-components-product-metadata dt').each(function() {
var label = $(this).text().trim();
if (label.toLowerCase().includes('custom name')) {
var valueElement = $(this).next('dd');
var metaValue = valueElement.text().trim();
if (valueElement.find('.edit-custom-name-btn-block').length === 0) {
var cartItem = $(this).closest('.wc-block-cart-item');
var cartItemIndex = $('.wc-block-cart-item').index(cartItem);
var editButton = $('<button/>', {
'type': 'button',
'class': 'edit-custom-name-btn edit-custom-name-btn-block',
'data-current-name': metaValue,
'data-cart-index': cartItemIndex,
'html': '✏️ Edit Name',
'css': {
'font-size': '12px',
'padding': '5px 10px',
'margin-left': '10px',
'cursor': 'pointer',
'background': '#0071a1',
'color': '#fff',
'border': 'none',
'border-radius': '4px'
}
});
valueElement.append(' ').append(editButton);
}
}
});
}
// Try multiple times with different delays
setTimeout(function() { addEditButtonsToBlocks(); }, 500);
setTimeout(function() { addEditButtonsToBlocks(); }, 1000);
setTimeout(function() { addEditButtonsToBlocks(); }, 2000);
// Re-add buttons when cart updates (block cart uses React)
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
setTimeout(addEditButtonsToBlocks, 100);
});
var cartContainer = document.querySelector('.wc-block-cart');
if (cartContainer) {
observer.observe(cartContainer, {
childList: true,
subtree: true
});
}
}
// Handle edit button click for blocks
$(document).on('click', '.edit-custom-name-btn-block', function(e) {
e.preventDefault();
e.stopPropagation();
var currentName = $(this).data('current-name');
var cartIndex = $(this).data('cart-index');
// Store the cart index for block updates
$('#edit-cart-key').val('block_' + cartIndex);
$('#edit-custom-name').val(currentName);
$('#update-message').hide();
$('#edit-name-modal').fadeIn();
// Store additional data for block update
$('#edit-name-form').data('is-block', true);
$('#edit-name-form').data('cart-index', cartIndex);
});
});
</script>
<?php
}
}
// 13. Enhanced AJAX handler for block-based cart
add_action('wp_ajax_update_cart_item_name', 'update_cart_item_name_ajax_enhanced', 5);
add_action('wp_ajax_nopriv_update_cart_item_name', 'update_cart_item_name_ajax_enhanced', 5);
function update_cart_item_name_ajax_enhanced() {
check_ajax_referer('update-cart-item-name', 'security');
$cart_item_key = sanitize_text_field($_POST['cart_key']);
$new_name = sanitize_text_field($_POST['custom_name']);
$is_block = isset($_POST['is_block']) ? (bool)$_POST['is_block'] : false;
$cart_index = isset($_POST['cart_index']) ? intval($_POST['cart_index']) : null;
$cart = WC()->cart->get_cart();
// For block-based cart, find item by index
if ($is_block && $cart_index !== null && $cart_index >= 0) {
$cart_items_array = array_values($cart);
$cart_keys_array = array_keys($cart);
// Check if index exists
if (isset($cart_items_array[$cart_index]) && isset($cart_keys_array[$cart_index])) {
$actual_key = $cart_keys_array[$cart_index];
// Update the cart item
$cart[$actual_key]['custom_name'] = $new_name;
WC()->cart->set_cart_contents($cart);
WC()->cart->calculate_totals();
wp_send_json_success(array(
'message' => __('Name updated successfully', 'woocommerce'),
'cart_key' => $actual_key,
'index' => $cart_index
));
return;
} else {
wp_send_json_error(array(
'message' => __('Cart item not found at index ' . $cart_index, 'woocommerce'),
'debug' => array(
'cart_index' => $cart_index,
'total_items' => count($cart_items_array),
'cart_keys' => $cart_keys_array
)
));
return;
}
}
// For classic cart
if (isset($cart[$cart_item_key])) {
$cart[$cart_item_key]['custom_name'] = $new_name;
WC()->cart->set_cart_contents($cart);
WC()->cart->calculate_totals();
wp_send_json_success(array('message' => __('Name updated successfully', 'woocommerce')));
} else {
wp_send_json_error(array(
'message' => __('Cart item not found', 'woocommerce'),
'debug' => array(
'cart_key' => $cart_item_key,
'available_keys' => array_keys($cart)
)
));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment