How to Add a Custom Field to a WooCommerce Product

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.

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.

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.

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.

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.

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.

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.

And finally, why not? Let’s add it to emails too:

/*
* 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.

Change the sort order of WooCommerce Grouped Products

WooCommerce has a small army of filters and hooks that you could customize almost every aspect of the plugin. If you have a grouped product the default display method is by menu_order. But recently, someone wanted to display the grouped items by the date they were published.

This is very easily accomplished by filtering the $args being passed through the woocommerce_grouped_children_args filter. WooCommerce queries for the child products in the grouped product via WP_Query so you can essentially use any parameter supported by WP_Query. In this case, we only need to change the orderby parameter to date and since we’d like the most recent items first, we swap the order parameter to descending.

If you need help understanding filters, I wrote what I think is a pretty good tutorial on how to use filters. It took me a while to understand them, but once you do they are very powerful and let you make a lot of customizations.

If you aren’t seeing any changes, chances are that the grouped product’s children have already been stored in a transient. You will need to delete this transient to see changes right away. You can clear all WooCommerce product transients via the Admin. Navigate to WooCommerce>System Status>Tools and click on the button to clear transients.

Modifying the WooCommerce Product Query

I recently answered a few questions on Stack Overflow and they all seemed to related to how to change what products show up in the shop loop… or any other of the shop archives. I figured I would compile them altogether for reference and inspiration.

WooCommerce builds a custom query for products in its WC_Query class by hooking into the classic pre_get_posts hook and changing WordPress’s query parameters to get the desired products. WooCommerce then removes itself from the query afterwards. I’m not 100% sure of why, but I presume there is a good reason. It might be running in WooCommerce, but it is still a regular WordPress query and so the regular WP_Query Parameters apply.

Like most WooCommerce code there is a convenient action hook right in the middle of the product_query() method which will allow us to hook in and make whatever changes we might like to the product query. Note that $q is the query and $this is the WC_Query class instance.

Example 1: Display Only On-Sale products in Shop

WooCommerce has a shortcode for displaying the most recent on-sale products. So I took a look at how it sets up it’s query args for get_posts(). Turns out WC has a built-in function that returns the IDs of any on-sale products: wc_get_product_ids_on_sale(). So we get grab those ids and then query for those specific posts using the post__in parameter of WP_Query.

As is typical in dealing with WordPress query objects we can use get() and set() methods to retrieve info about the query or set new parameters.

Example 2: Hide Products that are Part of a Grouped Product

The person who asked this question has a lot of products that part of other grouped products and didn’t want to display the items individually and in their grouped versions. Turns out that items that are part of a group have the grouped product as their post_parent. Any grouped product (or other top-level, non-grouped product) will have 0 as a post parent. Therefore, we can set the query argument to require a post parent of 0 and effectively eliminate any grouped items.

Example 3: Hide Specific Out of Stock Items

WooCommerce has a setting that allows you to hide all out of stock items from your store. But what if you don’t want a nuclear option and want to have a little finer control over which items are going to be hidden.

We can’t skip straight to the query, because WooCommerce doesn’t have this data. So we need to add some meta. You could use a custom field, but that isn’t as friendly as adding a little checkbox to the product data metabox. I thought it would be appropriate if we placed it near the stock status inputs. WooCommerce already has functions for creating most of the standard input types so we’ll use that to our advantage.

Then we need to save this data. Normally, I’d save a checkbox as ‘yes’ versus ‘no’ like WooCommerce does. However, getting the product query correct (as you’ll see a little later on), required that the meta exist when you wanted to hide the item and not exist at all otherwise… hence the if/else update_post_meta() versus delete_post_meta()

And now we can get to the query. Because WooCommerce already has a few keys in its default meta query and the default meta query operator is AND (meaning my conditions had to be met in addition to what WooCommerce was already looking for), I couldn’t figure out a way to query for posts that had meta equal to a specific key. But because WordPress supports EXISTS and NOT EXISTS comparisons, we can look for posts that way. What I’ve done is in the case where you aren’t mass hiding all out of stock items via the plugin option, this code will modify the meta query so that any item that does not have the meta key _hide_if_out_of_stock will be shown. That is a counter-intuitive way of saying that any product where the box “hide when out of stock” is checked will be hidden.

Those are the three uses of modifying WooCommerce’s product query that I have encountered in the past few days. Anybody else doing anything interesting to the product query?

WooCommerce Customize Checkout Fields

I was digging around in WooCommerce while trying to come up with a quote for a client. Part of the scope had me wondering if you could remove some fields from the checkout process. If you aren’t selling physical products a billing address can be too much information and could even be off-putting to potential customers.

I eventually tracked the fields down from the checkout template, to the checkout class to the get_address_fields() method in the WC_Countries class.

Removing Billing Address

Like a lot of things in WooCommerce you can modify values via filter. To remove all the physical address fields from the billing address here is the code I used:

Yes this could go in functions.php but that kind of locks you into a specific theme. If this is a permanent change then you probably want to create a site-specific plugin and put it in the wp-content/mu-plugins/ folder.

Add a Custom Checkout Field

While I was fooling around I figured I would see if I could add a custom checkout field. Turns out this is a bit more complex, but ultimately do-able. The following code will add the field to the checkout page, save the data to order meta and display the order meta in the orders admin.

Update: After receiving multiple contacts about adding more than one field I have modified the sample code to add 2 fields.

Note: there is no foreach() in the rest of the function so we must save and display each field individually

Save the extra data on checkout

Display the extra data to users

Display extra data in admin

Alternatively, display extra data as editable data (and save)

This should function like the shipping and billing address data and reveal inputs when the little pencil icon is clicked. Make sure to delete the previous function as two functions with the same name will cause a PHP error.

Add the field to order emails

The easiest way to add data to the emails is to add our meta to the list of meta that WooCommerce will print out automatically. For WooCommerce 2.3 and after, you will do it the following way:

If you’d rather customize the output in the emails you can add some text to any of the hooks available in the email templates.

Hope that helps you. I think there are a few plugins out there now if this was too intimidating, so I would suggest using one of those.

Note: This was tested with WooCommerce 2.2. Any prior or later versions, I can’t promise will be exactly the same.

WooCommerce Name Your Price 2.0 released!

In the summer of 2012, I was visiting one of my best friends in New Orleans… cooking lots of tacos and mixing lots of margaritas. While there, one of my earliest clients contacted me and wanted to be able to sell his books for whatever the customer was willing to pay. Did I know a way to do that with WooCommerce he asked? Well no, but I’m only working on my taco recipes at the moment so maybe I can look into it?

I started to poke around and ask some questions at Github and someone else contacted me to tell me that he’d buy this right away if it existed. I took these two people as the least-scientific version of “Idea Validation” imaginable and decided to give this thing a whirl.

About a month later I had a working prototype up and for sale at WooThemes. It only worked on simple products and then with a little more effort on subscriptions. I was pretty content to leave it at that, but one request kept coming back… can this work with variable products?

For a long time the answer was no, or maybe, or I’m working on it, but mostly no. You see, I’m also a semi-professional athlete so I am not a full-time coder… and variable products are complicated! But after an extremely long “beta” process I am pumped to announce that Name Your Price 2.0 now supports variable products! You can pick and choose which variations will be Name Your Price enabled, so you can all customers to set a price on all a product’s variations or just one. This feature does require WooCommerce 2.1 or greater. Version 2.0 was a pretty major overhaul, so be careful if you were overriding any templates. There’s some documentation coming on that.

Finally, Name Your Price for variable products
Finally, Name Your Price for variable products

So if this was holding you back from purchasing Name Your Price, head over to Woothemes and pick up your copy!

Important: Support requests are not handled in the comments and must go through WooThemes.