Add custom fields as cart item meta and order item meta in WooCommerce
Asked Answered
V

2

5

This is a plugin on how to add add cart item meta & order item meta for my WooCommerce order. Initially my code below worked well for input type=text. It returns the label for value and the inputed value.

On conversion to type=checkbox the code returns label and value="on" for those that are checked.

I would like to return the only value names of checked values (ignore the values unchecked).

A refactor to help include more checkboxes options would be helpful to reduce written code.

My code:

<?php
    global $woocommerce, $product, $post;

        add_action( 'woocommerce_before_add_to_cart_button', 'add_fields_before_add_to_cart' );

        function add_fields_before_add_to_cart( ) {
            ?>

            <div class="simple-selects">
                <div class="col-md-6">
                    <h3>Main meals</h3>
                    <p><input type="checkbox" name="mm_chicken_cutlet_bento" id="mm_chicken_cutlet_bento"><?php _e( "Chicken Cutlet Bento", "aoim"); ?></p>
                    <p><input type="checkbox" name="mm_roasted_pork_rib_bento" id="mm_roasted_pork_rib_bento"><?php _e( "Roasted Pork Rib Bento", "aoim"); ?></p>
                </div>
            </div>        

            <?php
        }

        /**
         * Add data to cart item
         */
        add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
        function add_cart_item_data( $cart_item_meta, $product_id ) {

            if ( isset( $_POST ['mm_chicken_cutlet_bento'] ) && isset( $_POST ['mm_roasted_pork_rib_bento'] ) ) {
                $custom_data  = array() ;
                $custom_data [ 'mm_chicken_cutlet_bento' ]    = isset( $_POST ['mm_chicken_cutlet_bento'] ) ?  sanitize_text_field ( $_POST ['mm_chicken_cutlet_bento'] ) : "" ;
                $custom_data [ 'mm_roasted_pork_rib_bento' ] = isset( $_POST ['mm_roasted_pork_rib_bento'] ) ? sanitize_text_field ( $_POST ['mm_roasted_pork_rib_bento'] ): "" ;
                $cart_item_meta ['custom_data']     = $custom_data ;
            }

            return $cart_item_meta;
        }

        /**
         * Display custom data on cart and checkout page.
         */
        add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 );
        function get_item_data ( $other_data, $cart_item ) {
            if ( isset( $cart_item [ 'custom_data' ] ) ) {
                $custom_data  = $cart_item [ 'custom_data' ];

                $other_data[] = array( 'name' => 'Chicken Cutlet Bento', 'display'  => $custom_data['mm_chicken_cutlet_bento'] );
                $other_data[] = array( 'name' => 'Roasted Pork Rib Bento', 'display'  => $custom_data['mm_roasted_pork_rib_bento'] );
            }

            return $other_data;
        }

        /**
         * Add order item meta.
         */
        add_action( 'woocommerce_add_order_item_meta', 'add_order_item_meta' , 10, 2);
        function add_order_item_meta ( $item_id, $values ) {
            if ( isset( $values [ 'custom_data' ] ) ) {
                $custom_data  = $values [ 'custom_data' ];
                wc_add_order_item_meta( $item_id, 'Chicken Cutlet Bento', $custom_data['mm_chicken_cutlet_bento'] );
                wc_add_order_item_meta( $item_id, 'Roasted Pork Rib Bento', $custom_data['mm_roasted_pork_rib_bento'] );
            }
        }

?>
Vanbuskirk answered 18/12, 2017 at 9:16 Comment(0)
H
13

Update (related to comments):

  • Limit the functionality to only one product ID
  • Add all checkboxes values as a coma separated string

To get easily the label names of your checkboxes as values and to "refactor to help include more checkboxes options would be helpful to reduce written code" I have added a simple function where you will set the key/value pairs for each checkbox you want to display and process…

So I have revisited all your code:

// HERE set the array of pairs keys/values for your checkboxes
function custom_checkboxes(){
    return array(
        'mm_chicken_cutlet_bento'       => __( "Chicken Cutlet Bento", "aoim"),
        'mm_roasted_pork_rib_bento'     => __( "Roasted Pork Rib Bento", "aoim"),
    );
}

// Displaying the checkboxes
add_action( 'woocommerce_before_add_to_cart_button', 'add_fields_before_add_to_cart' );
function add_fields_before_add_to_cart( ) {
    global $product;
    if( $product->get_id() != 2 ) return; // Only for product ID "2"

    ?>
    <div class="simple-selects">
        <div class="col-md-6">
            <h3><?php _e("Main meals", "aoim"); ?></h3>
            <?php foreach( custom_checkboxes() as $key => $value ): ?>
                <p><input type="checkbox" name="<?php echo $key; ?>" id="<?php echo $key; ?>"><?php echo ' ' . $value; ?></p>
            <?php endforeach; ?>
        </div>
    </div>
    <?php
}


// Add data to cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
function add_cart_item_data( $cart_item_data, $product_id ) {
    if( $product_id != 2 ) return $cart_item_data; // Only for product ID "2"

    // Set the data for the cart item in cart object
    $data = array() ;

    foreach( custom_checkboxes() as $key => $value ){
        if( isset( $_POST[$key] ) )
            $cart_item_data['custom_data'][$key] = $data[$key] = $value;
    }
    // Add the data to session and generate a unique ID
    if( count($data > 0 ) ){
        $cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
        WC()->session->set( 'custom_data', $data );
    }
    return $cart_item_data;
}


// Display custom data on cart and checkout page.
add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 );
function get_item_data ( $cart_data, $cart_item ) {
    if( $cart_item['product_id'] != 2 ) return $cart_data; // Only for product ID "2"

    if( ! empty( $cart_item['custom_data'] ) ){
        $values =  array();
        foreach( $cart_item['custom_data'] as $key => $value )
            if( $key != 'unique_key' ){
                $values[] = $value;
            }
        $values = implode( ', ', $values );
        $cart_data[] = array(
            'name'    => __( "Option", "aoim"),
            'display' => $values
        );
    }

    return $cart_data;
}

// Add order item meta.
add_action( 'woocommerce_add_order_item_meta', 'add_order_item_meta' , 10, 3 );
function add_order_item_meta ( $item_id, $cart_item, $cart_item_key ) {
    if ( isset( $cart_item[ 'custom_data' ] ) ) {
        $values =  array();
        foreach( $cart_item[ 'custom_data' ] as $key => $value )
            if( $key != 'unique_key' ){
                $values[] = $value;
            }
        $values = implode( ', ', $values );
        wc_add_order_item_meta( $item_id, __( "Option", "aoim"), $values );
    }
}

This code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Tested and works.


You will get something like this:

enter image description here

I have added "Option", as label to avoid the value repetition…

Hyperbaton answered 18/12, 2017 at 12:17 Comment(8)
The refactor works well. There a few tweeks to add. 1. Can this be restricted to one(only) product say to a product with a particular ID? 2. I have updated my question with image to show that I meant by "I would like to return the only value names of checked values (ignore the values unchecked)." I need to display these in the order table on checkout when they are just comma separated.Vanbuskirk
e.g if Chicken Cutlet Bento and Roasted Pork Rib Bento are checked as options, they should be listed under the order table as "Chicken Cutlet Bento , Roasted Pork Rib Bento, example 3, example 4" I edited the question with an image to show what I mean.Vanbuskirk
@Vanbuskirk I have updated the code for your first request… to limit this for one product only.Hyperbaton
I am still lost at your comment above. Maybe I am stuck in my workflow thought. Ideally we would just echo the "Chicken Cutlet Bento , Roasted Pork Rib Bento," with commas. No need to change the structure. I am bad with php arrays but we could restructure add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 ); function to that endVanbuskirk
@Vanbuskirk Ok let me try something…Hyperbaton
Thanks. i tried global $product; if( $product->get_id() != 2 ) return; // Only for product ID "2" I forgot to localize the global $product; to function.Vanbuskirk
@Vanbuskirk Ok updated and tested… It works as you expect now.Hyperbaton
Maybe, there's a mistake in your code under "Add the data to session and generate a unique ID" : if( count($data > 0 ) ) >>> if( count($data) > 0 )Oestrin
C
3

LoicTheAztec's solution mostly still works, but the action to add metadata from cart to order is deprecated now.

Instead of woocommerce_add_order_item_meta (deprecated) you can use woocommerce_checkout_create_order_line_item like so:

add_action(
    'woocommerce_checkout_create_order_line_item',
    function(WC_Order_Item_Product $cartItem, string $cartItemKey, array $values): void {
        if (!empty($values['custom_data']) && is_array($values['custom_data'])) {
            $values = [];
            foreach ($values['custom_data'] as $key => $value) {
                if ($key !== 'unique_key'){
                    $values[] = $value;
                }
            }
            $cartItem->add_meta_data(__('Option', 'aoim'), implode(', ', $values), true);
        }
    },
    10,
    3
);
Cher answered 13/10, 2021 at 12:21 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.