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.

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.

Add a Repeating Text Field to Options Framework Plugin

Options framework repeating field

Options framework repeating field
This is what we’ll be building.
I don’t hardly even know why I was working on this today.  Well a friend wanted to be able to control the radio buttons in a metabox, from the backend (since the otherwise brilliant WP-Alchemy is all dev-level coding) and for some reason  my brain went to theme option.  He ultimately went a different route and used Advanced Custom Fields.  But some days I just feel like not working and doing something fun, and the question of repeating fields in the theme options piqued my interest.  I asked Devin Price if his awesome Options Framework plugin has such a thing and he replied that no it didn’t exist…. yet. He wasn’t sure it was possible, which is like the worst thing to say to me. Of course it is possible! And now instead of working on something responsible, I will now solve this riddle. So I started digging into Options Framework. At first I thought that I would fork OF, but it turns out that all the functions/filters needed are already in place!   Wherever you have your $options array in your theme’s options.php to get a repeating field you’d define your custom option like so:

Then to handle all the parts of displaying this, scripting the repeat, and validating the data you’ll need the following group of functions, added to your theme’s functions.php.

Creating the Output

First, we need to output something when Options Framework gets running and encounters the custom option of type ‘repeat_text’. Devin has a catch-all filter already built-in, so we just have to attach a function to the {$option_type}_option_type filter, in this case repeat_text_option_type. This callback will create the input box (as many as needed by the array of values) plus one hidden one. It also creates the “Add New” button and for good fun we’ll throw in delete buttons next to each input. The markup (and later the script to handle the repeat) draws a lot of inspiration for WP Alchemy’s repeating groups.

Sanitation and Saving

Options Framework won’t save anything unless the data gets run through a sanitization filter. The neat thing, is that if you set up the input names as name="somefield[1]" and so on, WordPress will automatically save all the “somefield” fields in an array. So we just need to rifle through the array and sanitize each text field. We can do this quickly with array_map. Next

Style and Scripting the Repeating Fields

Finally, all the bells and whistles. What good is a repeating field if it doesn’t, you know, repeat. Again, I have to credit Dimas Begunoff for his WP Alchemy class. I’ve simplified some of what he was doing in metaboxes, by merely adding a data-rel to the hidden input. Then a few quick on handlers for the button click events and that hidden input we created earlier gets cloned and given a proper name attribute. Clicking a delete button, removes it’s associated input from the DOM.

Output

Because we’ve saved an array of data the if you try to echo out the results of of_get_option('example_repeat'); you will get Array. You have to loop through the array to print out each on individually with a foreach loop.

This is definitely beta-ish. For instance, I know it doesn’t take into account a way to set an array of defaults… like if you wanted to have two fields set up as the default. Maybe it is Google Chrome, but I’m also having a little issue with tab indexing. And finally… there was something else, but my mind just went blank. Get the full code from my Gist. As beta code, I am totally open to suggestions and improvements, but I won’t be able to help you set this up on your own sites. I’m just putting it out there for information purposes only. What would you use a repeating field for in theme options?

How to Use Multiple WYSIWYG (TinyMCE) Visual Editors in Your WordPress Metaboxes

Ever since WordPress invented the metabox, people have been trying to put the rich text editor (in WP this is provided by TinyMCE) into their metaboxes. And it makes sense. Sure us uber-geeks know the HTML tags required to make some text bold, but most of the world does not. But they do know how to click on the “B” button.  So if we’re building a theme that requires multiple blocks of content it’s nice to reuse the visual editor.

I’ve been using the WP Alchemy Class by Dimas Begunoff to build my metaboxes for a while now.  It gives a *lot *of power to build really complicated field sets, especially repeating ones.  For a long time using the visual editor in the metaboxes was elusive. Dimas wrote an article on How to Use Multiple WordPress WYSIWYG Visual Editors and it worked, up to a point.  Because TinyMCE is fickle (and despite their tagline being “Easy to Integrate”) this code didn’t work for repeating groups, which in my mind is the strength of Alchemy.

As of WordPress 3.3 you can finally use the built-in wp_editor() function for multiple editors in your metaboxes, but again these must be defined in advance.  You can’t use this function with WPA’s dynamically generated fields. So, repeatable, sortable WYSIWYG (TinyMCE) rich-text editors has been the elusive, ultimate dream of WP Alchemists. The <cough>Holy Grail</cough> of WP Alchemy. But after a lot of head-banging effort, I think I have it!

I have create a Twenty Eleven child theme that shows WP Alchemy-powered metaboxes with repeatable, sortable, WYSIWYG (tinyMCE-enabled) text editors complete with media buttons. And as a bonus, it shows a single field using wp_editor() because that also needed a little twist to work with WPA. Upload and activate the theme, then add a new post to see the boxes in action.  For the curious, and come on you know you are, the real magic is in the kia-metabox.js script. Download the sample theme from for an idea of how it works.

Fork me on github!  Perhaps we can figure out how to use the quicktags editor when the visual editor is disabled. Full tutorial to come…  maybe.

Create an Alphabetical Glossary of Posts in WordPress

Once up a time i did a client project where i had to have archives organized alphabetically.   I ended up accomplishing by adding a query variable and targeting the posts_where filter.  However, in answering a recent question at WordPress Stack Exchange I decided that it might be neater to create a hidden taxonomy instead.  I don’t know if there is any performance benefit, but the code was a little more elegant and you get prettier permalinks: site.com/glossary/a instead of site.com/?glossary=a right off the bat without the need to do any complicated htaccess rewrite rules, since everyone knows that mod-rewrite is straight up voodoo.

Creating a Hidden Taxonomy

Pretty standard function for registering a taxonomy. Just going to accept a lot of the defaults, and not worry about labels since we’re going to hide it from the back-end by setting the ‘show_ui’ parameter to false. For this example we’re going to call the taxonomy “glossary” and we’re going to assign it to posts, but we could well have done it for any custom post type.

Automatically Setting Terms for each Post on Save/Update

It’d be a pain if we had to actively remember to sort each post by its letter each time we wrote a post. I have enough difficulty just writing a post in the first place. But with some code we can automatically pop off the first letter of the post title and assign that letter as the term in our “glossary” taxonomy. The following is the pretty standard function for saving information from a metabox, which works perfectly for our case even though we don’t have a visible metabox.

Auto-Assigning Terms to existing posts

If you’ve had a blog for a while and have a lot of posts the idea of going back and manually saving the posts again sounds worse than pulling our your toenails.  So you can run a little function to do it automatically.  Ideally, if this were a plugin, it’d run on the plugin’s activation hook.  Since it’s not you can just drop it into your functions.php and then reload your site. Thanks to the transient check you can leave it in there and it won’t run again. Basically what it will do it grab all your posts, loop through them all, pop off the first letter of the title and assign it to the “glossary” taxonomy.

Finally, Creating the Alphabet “Menu”

Now that we have assigned a term for each existing post and for every post to come in our custom taxonomy, we should display an alphabet menu…. or some way to access the different Letter archives.  This was the one place were the code was not 10x more elegant than in my first iteration because there was no easy way to test whether a term had a post in it.  Usually, yes, it would… but if you changed a post name and there were no other posts under a certain letter you could come up with an empty section.  So what I did was get all the terms in the taxonomy with get_terms which hides empty terms by default, loop through that data and store it in an array, then check each letter of the alphabet against that array.  To avoid running through that every single page load, I used the transient API to store the resulting array of alphabet terms.  This array refreshes whenever a post is updated (this actually happens in an earlier code block for the kia_save_first_letter() function. For my original project I wanted to have the letters with posts have an active link and the letters without posts in that section just have the letter.  Something like: A B C D** E** …. and onwards, so I have re-created that effect.

Future Improvements

One thing I’d like to improve upon would be ensuring that the first letter I’m popping off with the substr PHP function is actually a letter… or maybe a number, but not a character.  I’d also like to get it to skip words like The, An, and other pointless words that don’t really reflect on the subject.  But then this isn’t a solution for everyone… if you want to make sure that “A Post about Bacon” and “The Bacon Post” show up in the same archive, well you should tag them in the Bacon tag and not hope that they show up alphabetically in the right place.  Still it has its uses.  Let me know of any improvements you make!