Search code examples
phpwordpresswoocommercehook-woocommercewoocommerce-subscriptions

Passing custom data from cart items to Order meta in Woocommerce 3


I have implemented a custom HTML Form and asking for some data which my customers will pass to place order successfully. Without these details my order has no importance.

For HTML form, I am referencing some custom PHP script which is below and which processes POST data from the Form and creates Cart with these data programmatically. Thanks @LoicTheAztec to help me achieve this.

The script.php file code:

<?php

    require_once("../wp-load.php");

    $customer_name = $_POST["customer_name"];
    $customer_email = $_POST["customer_email"];
    $customer_sex = $_POST["customer_sex"];
    $customer_age = $_POST["customer_age"];
    $product_id = $_POST["product_id"];

$custom_data = array(); // Initializing

    if( isset($_POST['customer_name']) && ! empty($_POST['customer_name']) )
        $custom_data['custom_data']['name']  =  $_POST['customer_name'];
    if( isset($_POST['customer_email']) && ! empty($_POST['customer_email']) )
        $custom_data['custom_data']['email'] = $_POST['customer_email'];
    if( isset($_POST['customer_sex']) && ! empty($_POST['customer_sex']) )
        $custom_data['custom_data']['sex']   = $_POST['customer_sex'];
    if( isset($_POST['customer_age']) && ! empty($_POST['customer_age']) )
        $custom_data['custom_data']['age']   = $_POST['customer_age'];

    global $woocommerce;

    if (WC()->cart->add_to_cart( $product_id, '1', '0', array(), $custom_data )) {
        var_dump($product_id);
    } else {
        var_dump($customer_name);
    }

    header("Location: ./checkout");
?>

As you see we have programmatically created a cart item using WC_Cart add_to_cart() method. So, all custom data is being saved to Cart as custom cart item data.

And now, we have also placed a code block into functions.php to print these data on Checkout page.

add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );

function my_custom_checkout_field( $checkout ) {

    //var_dump($checkout);
    global $woocommerce;

    echo '<div id="my_custom_checkout_field"><h2>' . __('Child Info') . '</h2>';

    foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
        if( isset($cart_item['custom_data']) ) {

            $custom_data = $cart_item['custom_data'];

            echo("<div>Name: <strong>" . $custom_data['name'] . "</strong></div>");
            echo("<div>Email: <strong>" . $custom_data['email'] . "</strong></div>");
            echo("<div>Gender: <strong>" . $custom_data['sex'] . "</strong></div>");
            echo("<div>Age: <strong>" . $custom_data['age'] . "</strong></div>");

        }

    }

    echo '</div>';

}

Now, I am trying to add these data printed on Checkout page to the Order page as well. As my order can't be completed without these data, user need to fill up these data and when he creates order, these data needs to be passed to the Order Summary page as well. And admin also needs to be able to see these data so he can process the order.

I hope this description clears everything and thanks again @LoicTheAztec to make me able to do this. Thank you very much.


Solution

  • Update 2 - Two steps

    1) Saving data:

    We will save this custom customer data as "Hidden" order "item" meta data and then as order meta data too as this is related to subscriptions with a unique order item:

    // Utility function: array of custom customer key/label pairs
    function custom_data_keys_labels(){
        return array(
            'name'  => __('Customer name'),     'email' => __('Customer email'),
            'sex'   => __('Customer gender'),   'age'   => __('Customer age'),
        );
    }
    
    // Add/save custom field value as custom HIDDEN order item meta data
    add_action( 'woocommerce_checkout_create_order_line_item', 'custom_field_update_order_item_meta', 20, 4 );
    function custom_field_update_order_item_meta( $item, $cart_item_key, $values, $order ) {
        if ( ! isset( $values['custom_data'] ) )
            return;
    
        $custom_data = $values['custom_data'];
        $meta_data   = array();
        $labels_keys = custom_data_keys_labels();
    
        foreach( $labels_keys as $key => $label ){
            if ( isset( $custom_data[$key] ) )
                $meta_data[$key] = $custom_data[$key];
        }
    
        if ( sizeof( $meta_data ) > 0 )
            $item->update_meta_data( __('_customer_data'), $meta_data );
    
        return $cart_item_data;
    }
    
    // Add/save custom fields values as custom order meta data
    add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 20, 2 );
    function my_custom_checkout_field_update_order_meta( $order, $data ) {
    
        $order_items = $order->get_items(); // Order itesm
        $item        = reset($order_items); // Keep only the first order item
    
        $item_data   = $item->get_meta( '_customer_data' ); // Get custom customer data
    
        if( is_array($item_data) && sizeof($item_data) > 0 ){
            foreach( $item_data as $key => $value ) {
                if ( isset( $item_data[$key] ) )
                    $order->update_meta_data( '_customer_' . $key, $value );
            }
            // Mark as data saved
            $order->update_meta_data( '_customer_data_set', true );
        }
    }
    

    Code goes in function.php file of your active child theme (or active theme). Tested and works.


    2) Displaying saved custom data:

    The code below also use our utility function custom_data_keys_labels()

    // Order pages (frontend and admin) display
    add_filter( 'woocommerce_order_details_after_order_table' , 'display_admin_order_meta_cutom_data', 20, 1 ); // Front
    add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_admin_order_meta_cutom_data', 20, 1 ); // Admin
    function display_admin_order_meta_cutom_data( $order ){
        $labels_keys = custom_data_keys_labels();
        if( $order->get_meta( '_customer_data_set' ) ){
            if( is_admin() ){ // Admin
                echo '<p>';
                foreach( $labels_keys as $key => $label ){
                    if ( $order->get_meta( '_customer_' . $key ) )
                        echo '<strong>' . $label . ':</strong> ' . $order->get_meta( '_customer_' . $key ) . '<br>';
                }
                echo '</p>';
            }
            else { // Front end: order view and Order received (thankyou)
                echo '<table class="woocommerce-table"><tbody>';
                foreach( $labels_keys as $key => $label ){
                    if ( $order->get_meta( '_customer_' . $key ) )
                        echo '<tr><th>' . $label . ':</th><td>' . $order->get_meta( '_customer_' . $key ) . '</td></tr>';
                }
                echo '</tbody></table>';
            }
        }
    }
    
    // Email notifications display
    add_filter( 'woocommerce_email_order_meta_fields' , 'display_email_cutom_data', 20, 3 );
    function display_email_cutom_data ( $fields, $sent_to_admin, $order ) {
        $labels_keys = custom_data_keys_labels();
        if( $order->get_meta( '_customer_data_set' ) ){
            foreach( $labels_keys as $key => $label ){
                $fields['customer_' . $key] = array(
                    'label' => $label,
                    'value' => $order->get_meta( '_customer_' . $key ),
                );
            }
        }
        return $fields;
    }
    

    Code goes in function.php file of your active child theme (or active theme). Tested and works.