MV-123
MV-123

Reputation: 377

WooCommerce : Add custom Metabox to admin order page

I am currently successfully adding a field to my WooCommerce product pages which is showing the value:

The problem: It isn't showing as a custom field in the admin order "custom fields" Metabox with the value inside it, but just as a text in the order page.

Here is my working code:

// Add the field to the product
add_action('woocommerce_before_add_to_cart_button', 'my_custom_checkout_field');

function my_custom_checkout_field() {
    echo '<div id="my_custom_checkout_field"><h3>'.__('My Field').'</h3>';
    echo  '<label>fill in this field</label> <input type="text" name="my_field_name">';
    echo '</div>';
}

// Store custom field
function save_my_custom_checkout_field( $cart_item_data, $product_id ) {
    if( isset( $_REQUEST['my_field_name'] ) ) {
        $cart_item_data[ 'my_field_name' ] = $_REQUEST['my_field_name'];
        /* below statement make sure every add to cart action as unique line item */
        $cart_item_data['unique_key'] = md5( microtime().rand() );
    }
    return $cart_item_data;
}
add_action( 'woocommerce_add_cart_item_data', 'save_my_custom_checkout_field', 10, 2 );

// Render meta on cart and checkout
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
    $custom_items = array();
    /* Woo 2.4.2 updates */
    if( !empty( $cart_data ) ) {
        $custom_items = $cart_data;
    }
    if( isset( $cart_item['my_field_name'] ) ) {
        $custom_items[] = array( "name" => 'My Field', "value" => $cart_item['my_field_name'] );
    }
    return $custom_items;
}
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );

// This is what I think needs changing?

function subscription_order_meta_handler( $item_id, $values, $cart_item_key ) {
    if( isset( $values['my_field_name'] ) ) {
        wc_add_order_item_meta( $item_id, "My Field", $values['my_field_name'] );
    }
}
add_action( 'woocommerce_add_order_item_meta', 'subscription_order_meta_handler', 1, 3 );

I think it is this last bit of the code that needs changing. It currently shows the text under the order item, so perhaps I need to adjust wc_add_order_item_meta to something else?

I've tried everything but it doesn't seem to work. I can get it to work when my field is on the checkout page but not when I pull it from the product page.

Perhaps I am missing a checkout process snippet?

Upvotes: 18

Views: 35829

Answers (1)

LoicTheAztec
LoicTheAztec

Reputation: 254373

UPDATE 2023 (Still works in Woocommerce 3+)

Firstly, I have gotten everything working as expected, except getting the value for my_field_slug in the back end "Custom fields" Metabox within Order pages.

Then after a real nightmare, I have found a pretty nice working solution, better than before. In the back end you have now a Custom Metabox with the custom field my_field_slug displaying the right value, like in this screenshot:

enter image description here


My code is divided into 2 parts.

  1. The backend Metabox in Order pages, with an editable field showing the correct value coming from a custom field on the product pages (in the front end):
// Adding Meta container admin shop_order pages
add_action( 'add_meta_boxes', 'add_shop_order_meta_boxes' );
if ( ! function_exists( 'add_shop_order_meta_boxes' ) )
{
    function add_shop_order_meta_boxes()
    {
        add_meta_box( 'custom_other_field', __('My Field','woocommerce'), 'add_custom_other_field_content', 'shop_order', 'side', 'core' );
    }
}

// Adding Meta field in the meta container on admin shop_order pages
if ( ! function_exists( 'add_custom_other_field_content' ) )
{
    function add_custom_other_field_content( $post )
    {
        $order = wc_get_order($post->ID); // Get the WC_Order object

        printf('<p style="border-bottom:solid 1px #eee;padding-bottom:13px;">
            <input type="hidden" name="custom_other_field_nonce" value="%s">
            <input type="text" style="width:250px;" name="my_field_slug" placeholder="%s" value="%s">
            </p>',
            wp_create_nonce(),
            __('some placeholder text', 'woocommerce'),
            $order->get_meta('_my_field_slug')
        );

    }
}
// Save the Meta field value
add_action( 'woocommerce_process_shop_order_meta', 'save_custom_metabox_field_value', 10, 2 );
if ( ! function_exists( 'save_custom_metabox_field_value' ) )
{
    function save_custom_metabox_field_value( $order_id, $post) {
        // Check if our nonce is set.
        if ( ! isset($_REQUEST['custom_other_field_nonce']) && ! wp_verify_nonce($_REQUEST['custom_other_field_nonce']) ) {
            return $order_id;
        }

        $order = wc_get_order($order_id); // Get the WC_Order object

        // Sanitize text input value and update the meta field in the database.
        $order->update_meta_data('_my_field_slug', sanitize_text_field($_POST['my_field_slug']));
        $order->save();
    }
}

  1. Frontend / Backend:

Note: We allow only one item in cart, as the product field value populates the order Metabox field.

• The product page custom field (front end).
• Displaying this data on cart, checkout and orders (front end). On email notifications too.
• Displaying data on the order page (back end)

// Display a text field on single product pages
add_action('woocommerce_before_add_to_cart_button', 'display_custom_field_on_single_product');
function display_custom_field_on_single_product() {
    echo '<div id="my_custom_field">
        <label>' . __( 'My Field') . ' </label>
        <input type="text" name="my_field_slug" value="">
    </div><br>';
}

// Allow only one item in cart
add_filter( 'woocommerce_add_to_cart_validation', 'allow_only_one_cart_item', 10, 3 );
function allow_only_one_cart_item( $passed, $product_id, $quantity ) {
    if( ! WC()->cart->is_empty() ) {
        WC()->cart->empty_cart();
    }
    return $passed;
}

// Store custom field value
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_field_as_custom_cart_item_data', 10, 2 );
function add_custom_field_as_custom_cart_item_data( $cart_item_data, $product_id ) {
    if( isset( $_REQUEST['my_field_slug'] ) ) {
        $cart_item_data[ 'my_field_slug' ] = sanitize_text_field($_REQUEST['my_field_slug']);
        // below statement make sure every add to cart action as unique line item
        $cart_item_data['unique_key'] = md5( microtime().rand() );
    }
    return $cart_item_data;
}

// Render the field value on cart and checkout
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
    $custom_items = array();

    if( !empty( $cart_data ) ) {
        $custom_items = $cart_data;
    }

    if( isset( $cart_item['my_field_slug'] ) ) {
        $custom_items[] = array( 
            'name'  => __('My Field'), 
            'value' => $cart_item['my_field_slug'] 
        );
    }
    return $custom_items;
}

// Save the the custom field value as order custom meta
add_action( 'woocommerce_checkout_create_order', 'save_custom_field_as_order_meta', 10, 2 );
function save_custom_field_as_order_meta( $order, $data ) {
    $items = WC()->cart->get_cart();
    $item  = reset($items);

    if ( isset($item['my_field_slug']) ) {
        $order->update_meta_data( '_my_field_slug', $item['my_field_slug'] );
    }
}

// Save the the custom field value as order item custom meta
add_action( 'woocommerce_checkout_create_order_line_item', 'save_custom_field_as_order_item_meta', 10, 4 );
function save_custom_field_as_order_item_meta( $item, $cart_item_key, $values, $order ) {
    if ( isset($values['my_field_slug']) ) {
        $item->update_meta_data( 'my_field_slug', $values['my_field_slug'] );
    }
}

 // Add readable "meta key" label name replacement
 add_filter('woocommerce_order_item_display_meta_key', 'filter_wc_order_item_display_meta_key', 10, 3 );
 function filter_wc_order_item_display_meta_key( $display_key, $meta, $item ) {
     if( $display_key === 'my_field_slug' ) {
         $display_key = __('My field', 'woocommerce');
     }
     return $display_key;
 }

Everything is working as expected now.

Upvotes: 54

Related Questions