In this WooCommerce tutorial I will be showing you how to add a custom field to the front-end of a WooCommerce product. We’ll be adding a text input that a customer could use to enter some special instructions or a custom inscription, etc. In theory, you could expand this to do all kinds of customizations (like allow the customer to upload an image), but this is a tutorial so let’s keep it kind of simple. Or in lieu of banging your head against a wall you could just buy WooCommerce Product Add-ons.
We’ll start with adding the input to the single product page template then add the custom text to the cart, order, and even the checkout emails!
Please note that all code could go in your theme’s functions.php
but really, this is functionality, so please put it in a plugin! This is going to be pretty code-heavy, so if you need a refresher on actions and filters and the like then you might want to review the basics before diving in to this.
First step is to add the text input to the front end
You can technically add this input anywhere, but since it is a core WooCommerce hook, woocommerce_before_add_to_cart_button
is about 99% likely to work with any theme. Nothing too special going on here. We’re just adding a text input. Pay attention the input’s name
. We’re going to be using that a lot.
1 2 3 4 5 6 7 8 9 10 |
/* * Display input on single product page * @return html */ function kia_custom_option(){ $value = isset( $_POST['_custom_option'] ) ? sanitize_text_field( $_POST['_custom_option'] ) : ''; printf( '<label>%s</label><input name="_custom_option" value="%s" />', __( 'Enter your custom text', 'kia-plugin-textdomain' ), esc_attr( $value ) ); } add_action( 'woocommerce_before_add_to_cart_button', 'kia_custom_option', 9 ); |
Validate and sanitize the input data
If your field is optional, then you can delete this function completely. Or you could modify it to validate however, you’d like. For simplicity’s sake I’ve triggered an error if the customer tries to add the item to the cart without filling in any custom text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Validate when adding to cart * @param bool $passed * @param int $product_id * @param int $quantity * @return bool */ function kia_add_to_cart_validation($passed, $product_id, $qty){ if( isset( $_POST['_custom_option'] ) && sanitize_text_field( $_POST['_custom_option'] ) == '' ){ $product = wc_get_product( $product_id ); wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter some custom text.', 'kia-plugin-textdomain' ), $product->get_title() ), 'error' ); return false; } return $passed; } add_filter( 'woocommerce_add_to_cart_validation', 'kia_add_to_cart_validation', 10, 3 ); |
Add the custom data to the cart item
At first there’s a lot of mystery going on with the cart. Where the heck is that data coming from anyway? All the products are stored in an array in _$SESSION
data. For the most part, Woo saves the product ID and the quantity and a handful of other things, but conveniently has a filter that will allow us to pass some of our own data to the cart item.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * Add custom data to the cart item * @param array $cart_item * @param int $product_id * @return array */ function kia_add_cart_item_data( $cart_item, $product_id ){ if( isset( $_POST['_custom_option'] ) ) { $cart_item['custom_option'] = sanitize_text_field( $_POST['_custom_option'] ); } return $cart_item; } add_filter( 'woocommerce_add_cart_item_data', 'kia_add_cart_item_data', 10, 2 ); |
Preserve the Cart Data
The cart is reloaded from the $_SESSION
on every page load. This must be a security feature, but I am not actually 100% sure. I do know that the first time I started messing around I didn’t understand why the previous function was adding the info to the cart, but as soon as I loaded the cart it disappeared. That drove me crazy for a bit until someone pointed out the woocommerce_get_cart_item_from_session
filter. Basically, we’ll just check if we already had the data in the $cart_item
array, and if so, maintain it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * Load cart data from session * @param array $cart_item * @param array $other_data * @return array */ function kia_get_cart_item_from_session( $cart_item, $values ) { if ( isset( $values['custom_option'] ) ){ $cart_item['custom_option'] = $values['custom_option']; } return $cart_item; } add_filter( 'woocommerce_get_cart_item_from_session', 'kia_get_cart_item_from_session', 20, 2 ); |
Save the Custom Data On Checkout
WooCommerce has improved quite a bit in how it handles this data. Now we can call a simple woocommerce_add_order_item_meta()
and it kind of acts like post meta, but for the item in this specific order. The data ends up in its own table.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * Add meta to order item * @param int $item_id * @param array $values * @return void */ function kia_add_order_item_meta( $item_id, $values ) { if ( ! empty( $values['custom_option'] ) ) { woocommerce_add_order_item_meta( $item_id, 'custom_option', $values['custom_option'] ); } } add_action( 'woocommerce_add_order_item_meta', 'kia_add_order_item_meta', 10, 2 ); |
Display all the Things!
Now that we actually have some usable data in the cart, it is time to display it to the customer. First, we’ll want to show it in the cart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * Get item data to display in cart * @param array $other_data * @param array $cart_item * @return array */ function kia_get_item_data( $other_data, $cart_item ) { if ( isset( $cart_item['custom_option'] ) ){ $other_data[] = array( 'name' => __( 'Your custom text', 'kia-plugin-textdomain' ), 'value' => sanitize_text_field( $cart_item['custom_option'] ) ); } return $other_data; } add_filter( 'woocommerce_get_item_data', 'kia_get_item_data', 10, 2 ); |
Then we’ll want to show it in the order overview page, which should also be the same template shown in the My Account area.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * Show custom field in order overview * @param array $cart_item * @param array $order_item * @return array */ function kia_order_item_product( $cart_item, $order_item ){ if( isset( $order_item['custom_option'] ) ){ $cart_item_meta['custom_option'] = $order_item['custom_option']; } return $cart_item; } add_filter( 'woocommerce_order_item_product', 'kia_order_item_product', 10, 2 ); |
And finally, why not? Let’s add it to emails too:
1 2 3 4 5 6 7 8 9 10 11 |
/* * Add the field to order emails * @param array $keys * @return array */ function kia_email_order_meta_fields( $fields ) { $fields['custom_field'] = __( 'Your custom text', 'kia-plugin-textdomain' ); return $fields; } add_filter('woocommerce_email_order_meta_fields', 'kia_email_order_meta_fields'); |
Bonus, Order Again
Should the customer want to order the exact same item with the exact same field we can do that too by adding the order item meta to the new cart item created when ordering again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * Order Again * @param array $cart_item * @param array $order_item * @param obj $order * @return array */ function kia_order_again_cart_item_data( $cart_item, $order_item, $order ){ if( isset( $order_item['custom_option'] ) ){ $cart_item_meta['custom_option'] = $order_item['custom_option']; } return $cart_item; } add_filter( 'woocommerce_order_again_cart_item_data', 'kia_order_again_cart_item_data', 10, 3 ); |