Adding of custom cart item data via Ajax Add to Cart

I had the same problem and just happened to have figured out a way – probably not the best but works for now.

These are the steps I took to see how it was done behind the scenes without doing any AJAX:

  1. I first I had to dump $_POST on the
    woocommerce_add_to_cart_validation hook to see how the plugin was sending its data. It was doing something like:

    [recipient_name] => Array
    (
        [12345] => 
        [67890] => John Smith
    )
    
    [recipient_email] => Array
    (
        [12345] => 
        [67890] => [email protected]
    )
    
    [message] => Array
    (
        [12345] => 
        [67890] => Enjoy your voucher!
    )
    

The number keys are the variation IDs.

  1. Then I dumped the cart data to see how it had been saved to the cart:

    add_action( 'woocommerce_after_cart', 'but_whats_in_the_cart' );
    function but_whats_in_the_cart() {
        $cart = WC()->cart->get_cart();
        echo '<pre>';
        print_r($cart);
        echo '</pre>';
    }
    

    And it gave me this:

    Array
        (
        [825d53934cc8498d59e3e553a5afa896] => Array
        (
            [voucher_template_id] => 27
            [voucher_image_id] => 378932
            [voucher_random] => voucher_5bc8babc5dee6
            [voucher_item_meta_data] => Array
            (
                [recipient_name] => John Smith
                [recipient_email] => [email protected]
                [message] => Enjoy your voucher!
            )
    ... array continues.
    
  2. So on my AJAX call I made sure I sent those three pieces of data and
    then formatted and put them in an array with key
    [voucher_item_meta_data].

The whole ‘Add to cart’ bit on the site I’m building is quite specific because instead of showing the variations in the standard WooCommerce style (in a select element), I have them in a list and users just check the variations they want, their quantity and add them all to the cart at once. All the data I’m passing is on data attributes and the PDF Voucher form is custom. Basically, I’m not using the WooCommerce add to cart form at all.

But in your example you’ll need to grab the PDF Voucher form data and send it via AJAX, maybe like this?

var recipient_name = $('#selector_for_name').val();
var recipient_email = $('#selector_for_email').val();
var message = $('#selector_for_message').val();

Then, the WP AJAX is the tricky bit. I could not find a “proper” way to pass data to the cart via AJAX. NOT data for a product, but cart data for each cart key.

I found some post about having to loop through the cart keys and add data this way so I gave it a try and it worked.

So, having passed your form data as above, you can then do this:

function nerb_ajax_add_to_cart_woo() {
// declare global
global $woocommerce;

$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
$product = wc_get_product( $product_id );
$quantity = empty( $_POST['quantity'] ) ? 1 : apply_filters( 'woocommerce_stock_amount', $_POST['quantity'] );
$variation_id = $_POST['variation_id'];
$variations  = $_POST['variation'];
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations, $cart_item_data );

// pdf voucher form data
$pdf_form_data['recipient_name'] = $_POST['recipient_name'];
$pdf_form_data['recipient_email'] = $_POST['recipient_email'];
$pdf_form_data['message'] = $_POST['message'];

if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
    do_action( 'woocommerce_ajax_added_to_cart', $product_id );

    /* HERE IS THE BIT THAT DOES THE TRICK */
    // you need to cycle the cart replace the meta of the correspondant key
    $cart = $woocommerce->cart->cart_contents;
    foreach ($cart as $key => $item) {
        // check if voucher meta already exists for an item, and if so, skip
        if($item['voucher_item_meta_data']) {
            continue;
        } else {
          // make sure we only add voucher data to the variation just added and only if data isn't there already so we don't override previously added ones
            if( $item['variation_id'] === $variation_id && !isset($item['voucher_item_meta_data']) ) {
                $woocommerce->cart->cart_contents[$key]['voucher_item_meta_data'] = $pdf_form_data;
            }
        }
    }

    // This is the magic: With this function, the modified object gets saved.
    $woocommerce->cart->set_session();

    
    if ( get_option( 'woocommerce_cart_redirect_after_add' ) == 'yes' ) {
        wc_add_to_cart_message( $product_id );
    }
    // Return fragments
    WC_AJAX::get_refreshed_fragments();
} 
die();  
}

add_action( 'wp_ajax_nerb_ajax_add_to_cart_woo', 'nerb_ajax_add_to_cart_woo' );
add_action( 'wp_ajax_nopriv_nerb_ajax_add_to_cart_woo', 'nerb_ajax_add_to_cart_woo' );

I have not tested this code with your current set up, but this is more or less what I have done and it has worked for me.

UPDATE – 19 Oct 2018

I found a bug and have updated the PHP code above. Since we’re first adding the product and then adding the voucher meta to the cart, when adding a second product the code was replacing the voucher meta data of the first product with the one from the second one (and so on). So we just need to check for the array key voucher_item_meta_data and if it exists, skip it:

// check if voucher meta already exists for an item, and if so, skip
if($item['voucher_item_meta_data']) {
    continue;
} else {
    // make sure we only add voucher data to the variation just added and only if data isn't there already so we don't override previously added ones
    if( $item['variation_id'] === $variation_id && !isset($item['voucher_item_meta_data']) ) {
        $woocommerce->cart->cart_contents[$key]['voucher_item_meta_data'] = $cart_item_data;
    }
}