So yesterday we briefly went over custom fields in this article. Today we are going to expand on that with some meta boxes. Meta boxes are really just a fancy way to input custom fields. They are less confusing for you and anyone else using your theme, as they can prompt for exactly what is needed. These can be text boxes, text areas, check boxes, etc. It’s up to you how you implement them. I only recently started using meta boxes, but I love them. Let’s take a look at how to use them.
First off I’m going to explain some code, and then give the full block of code. For this first portion we will be staying with the theme started yesterday, building a link input section for our new link post format, to link the post title to the URL of the article we are discussing. What we do today will get everything in the back end working to be able to add the url. It won’t actually do anything yet. Very soon we will cover how to get the function into your actual post. But first we need to set things up. If you want to jump the gun, yesterday’s post covered the basics of displaying meta information from custom fields. Since meta boxes really only input custom fields, you use the info the same way.
The first thing we are going to do is create the action using add_action. We add_action to add_meta_boxes which we’ve called voodoo_meta_box here. And we define the callback to the function which will create our meta boxes, which I’ve named voodoo_title_link. Here’s the first part:
// ADD ALL OUR META BOX STUFF
add_action('add_meta_boxes', 'voodoo_meta_box');
function voodoo_meta_box() {
add_meta_box('voodoo_title_link', 'Link Format Title URL', 'voodoo_title_link', 'post', 'side', 'default');
}
That gets things added in, but now we actually need to define voodoo_title_link, which is here:
// SET UP THE TITLE LINK META BOX
function voodoo_title_link() {
global $post;
// NONCENAME NEEDED TO VERIFY WHERE THE DATA ORIGINATED
echo '<input type="hidden" name="voodoo_meta_noncename" id="voodoo_meta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// GET EXISTING DATA IF IT'S ALREADY BEEN ENTERED
$vetitleurl = get_post_meta($post->ID, '_vetitleurl', true);
// ECHO OUT THE DISPLAY
echo '<p>URL to Link Posts Title To</p>';
echo '<input type="text" name="_vetitleurl" value="' . $vetitleurl . '" size="40" />';
}
So you’ll see on line 3 we global this, so we make it available in the loop. Lines 6 and 7 are verification. Basically we set up a nonce name in line 6, and use wp_create_nonce in line 7. This basically sets up a one time token which we authenticate later on, to verify the source of the input. You can read up in the article I linked. It would be great for you to understand it, but not imperitive. I don’t fully understand it, I just know how to use it for verification. So as long as you can copy/paste, you are good to go.
In this example, our custom field we are inputting to (the key) is called _vetitleurl. Why the underscore? That hides it from the custom fields mene on the backend. If you don’t put that there, then any custom fields you establish using meta boxes will also display in your custom fields menu on the post screen. It can get cluttered. I think there is no reason to see them in the menu since we are making these meta boxes to do the work.
So what line 10 does, is cycle through your post meta data. It’s basically checking to see if information has been entered before. If it has, it’s assigning that information to the $vetitleurl. Then on line 14 we are using that. Line 13 and 14 set up the actual display of our meta boxes. Line 13 being the title and instructions, and line 14 controlling the input box. You can see the input boxes name is our custom field name, and the value is $vetitleurl, or variable. That’s what makes any existing data appear.
So there you can see it so far. The key/value pair now exists. _vetitleurl is the key, and $vetitleurl is the variable that holds the value for it.
Let’s move on to the last chunk of code. This is a biggie, and I won’t necessarily be able to explain it all that well. But basically now we take all the inputs for any meta boxes that exist, verify their source by using wp_verify_nonce, throw all the information into an array, and do various checks on it before saving. Here is that bit of code for the function we are calling voodoo_save_meta:
// GOTTA SAVE THE METADATA OF COURSE
function voodoo_save_meta($post_id, $post) {
// CHECK THIS CAME FROM THE RIGHT SCREEN WITH PROPER AUTHENTICATION
// SINCE SAVE POST CAN BE TRIGGERED OTHER WAYS
if ( !wp_verify_nonce( $_POST['voodoo_meta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// CAN WE EDIT THE POST OR PAGE?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// AFTER AUTHENTICATION WE NEED TO FIND AND SAVE THE DATA
// PUT IT IN AN ARRAY TO MAKE IT EASIER TO LOOP THROUGH
$voodoo_meta['_vetitleurl'] = $_POST['_vetitleurl'];
// ADD VALUES OF $voodoo_meta AS CUSTOM FIELDS
foreach ($voodoo_meta as $key => $value) { // LOOP THROUGH THE $voodoo_meta ARRAY!
if( $post->post_type == 'revision' ) return; // DON'T STORE THE DATA TWICE
$value = implode(',', (array)$value); // IF $value IS IN AN ARRAY, MAKE IT A CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // IF CUSTOM FIELD ALREADY HAS A VALUE
update_post_meta($post->ID, $key, $value);
} else { // AND IF IT DOESN'T ALREADY HAVE A VALUE
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // DELETE IF IT'S EMPTY
}
}
add_action('save_post', 'voodoo_save_meta', 1, 2); // SAVE THE CUSTOM FIELDS
And once the voodoo_save_meta function is built, you can see on the last line we just hook into save_post to store our information.
That is a brief description of how it works. Honestly while learning to add meta boxes, I just copied and pasted for a long time before learning what anything does. So if that describes you, I do recommend you take a look at the links I gave for a general understanding. But here is the full block of code.
// ADD ALL OUR META BOX STUFF
add_action('add_meta_boxes', 'voodoo_meta_box');
function voodoo_meta_box() {
add_meta_box('voodoo_title_link', 'Link Format Title URL', 'voodoo_title_link', 'post', 'side', 'default');
}
// SET UP THE TITLE LINK META BOX
function voodoo_title_link() {
global $post;
// NONCENAME NEEDED TO VERIFY WHERE THE DATA ORIGINATED
echo '<input type="hidden" name="voodoo_meta_noncename" id="voodoo_meta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// GET EXISTING DATA IF IT'S ALREADY BEEN ENTERED
$vetitleurl = get_post_meta($post->ID, '_vetitleurl', true);
// ECHO OUT THE DISPLAY
echo '<p>URL to Link Posts Title To</p>';
echo '<input type="text" name="_vetitleurl" value="' . $vetitleurl . '" size="40" />';
}
// GOTTA SAVE THE METADATA OF COURSE
function voodoo_save_meta($post_id, $post) {
// CHECK THIS CAME FROM THE RIGHT SCREEN WITH PROPER AUTHENTICATION
// SINCE SAVE POST CAN BE TRIGGERED OTHER WAYS
if ( !wp_verify_nonce( $_POST['voodoo_meta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// CAN WE EDIT THE POST OR PAGE?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// AFTER AUTHENTICATION WE NEED TO FIND AND SAVE THE DATA
// PUT IT IN AN ARRAY TO MAKE IT EASIER TO LOOP THROUGH
$voodoo_meta['_vetitleurl'] = $_POST['_vetitleurl'];
// ADD VALUES OF $voodoo_meta AS CUSTOM FIELDS
foreach ($voodoo_meta as $key => $value) { // LOOP THROUGH THE $voodoo_meta ARRAY!
if( $post->post_type == 'revision' ) return; // DON'T STORE THE DATA TWICE
$value = implode(',', (array)$value); // IF $value IS IN AN ARRAY, MAKE IT A CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // IF CUSTOM FIELD ALREADY HAS A VALUE
update_post_meta($post->ID, $key, $value);
} else { // AND IF IT DOESN'T ALREADY HAVE A VALUE
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // DELETE IF IT'S EMPTY
}
}
add_action('save_post', 'voodoo_save_meta', 1, 2); // SAVE THE CUSTOM FIELDS
The cool thing about this, is that once you have this code in place, it isn’t too hard to add more and more meta boxes. You can recycle some code, and some code (like the save routine) only needs to be there once to work for multiple meta boxes. Let’s take a look at a much larger block of code from my blog.
// FUNCTION TO ADD ALL OUR METABOXES WHERE WE WANT THEM
add_action('add_meta_boxes', 've_custom_meta_boxes');
function ve_custom_meta_boxes() {
add_meta_box('ve_social_links', 'Social Links', 've_social_links', 've_members', 'side', 'default');
add_meta_box('ve_titleurl_links', 'Link Format Title URL', 've_titleurl_links', 'post', 'normal', 'default');
$yes_value = __( 'Yes', 'voodoo_lion' );
if( $yes_value == get_theme_option( 'include_honey' ) )
add_meta_box('ve_post_honey', 'Honey Pot Links', 've_post_honey', 'post', 'normal', 'default');
}
// OUR SOCIAL ICONS METABOXES
function ve_social_links() {
global $post;
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="ve_meta_noncename" id="ve_meta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// GET THE SOCIAL MEDIA INFO THAT HAS BEEN ENTERED ALREADY
$twitter = get_post_meta($post->ID, '_twitter', true);
$facebook = get_post_meta($post->ID, '_facebook', true);
$flickr = get_post_meta($post->ID, '_flickr', true);
$youtube = get_post_meta($post->ID, '_youtube', true);
$wordpress = get_post_meta($post->ID, '_wordpress', true);
$myspace = get_post_meta($post->ID, '_myspace', true);
// ECHO OUT THE FIELDS, WITH AN DATA THAT MAY ALREADY EXIST
echo '<p>Twitter Username</p>';
echo '<input type="text" name="_twitter" value="' . $twitter . '" size="40" />';
echo '<p>FaceBook Username</p>';
echo '<input type="text" name="_facebook" value="' . $facebook . '" size="40" />';
echo '<p>Flickr Username</p>';
echo '<input type="text" name="_flickr" value="' . $flickr . '" size="40" />';
echo '<p>YouTube Username</p>';
echo '<input type="text" name="_youtube" value="' . $youtube . '" size="40" />';
echo '<p>WordPress URL (no http://)</p>';
echo '<input type="text" name="_wordpress" value="' . $wordpress . '" size="40" />';
echo '<p>MySpace Username</p>';
echo '<input type="text" name="_myspace" value="' . $myspace . '" size="40" />';
}
// OUR TITLE URL METABOX
function ve_titleurl_links() {
global $post;
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="ve_meta_noncename" id="ve_meta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// GET THE TITLE URL IF ONE HAS ALREADY BEEN ENTERED
$vetitleurl = get_post_meta($post->ID, '_vetitleurl', true);
// ECHO OUT THE FIELD, WITH ANY DATA THAT MAY ALREADY EXIST
echo '<p>URL to Link Posts Title To</p>';
echo '<input type="text" name="_vetitleurl" value="' . $vetitleurl . '" size="60" />';
}
// OUR HONEYPOT CODE METABOX
function ve_post_honey() {
global $post;
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="ve_meta_noncename" id="ve_meta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// GET ANY HONEYPOT CODE ALREADY ENTERED
$veposthoney = get_post_meta($post->ID, '_veposthoney', true);
// ECHO OUT THE FIELDS WITH ANY EXISTING CODE
echo '<p>URL Code for Honey Pot Link</p>';
echo '<textarea name="_veposthoney" cols="100" rows="4" style="97%">' . $veposthoney . '</textarea>';
}
// SAVE THE DATA FOR ALL OF OUR METABOXES
function voodoo_save_meta($post_id, $post) {
// CHECK THIS CAME FROM THE RIGHT SCREEN WITH PROPER AUTHENTICATION
// SINCE SAVE POST CAN BE TRIGGERED OTHER WAYS
if ( !wp_verify_nonce( $_POST['ve_meta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// CAN WE EDIT THE POST OR PAGE?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// AFTER AUTHENTICATION WE NEED TO FIND AND SAVE THE DATA
// PUT IT IN AN ARRAY TO MAKE IT EASIER TO LOOP THROUGH
$voodoo_meta['_twitter'] = $_POST['_twitter'];
$voodoo_meta['_facebook'] = $_POST['_facebook'];
$voodoo_meta['_flickr'] = $_POST['_flickr'];
$voodoo_meta['_youtube'] = $_POST['_youtube'];
$voodoo_meta['_wordpress'] = $_POST['_wordpress'];
$voodoo_meta['_myspace'] = $_POST['_myspace'];
$voodoo_meta['_vetitleurl'] = $_POST['_vetitleurl'];
$voodoo_meta['_veposthoney'] = $_POST['_veposthoney'];
// ADD VALUES OF $voodoo_meta AS CUSTOM FIELDS
foreach ($voodoo_meta as $key => $value) { // CYCLE THROUGH $voodoo_meta ARRAY!
if( $post->post_type == 'revision' ) return; // WE DON'T WANT IT STORED TWICE
$value = implode(',', (array)$value); // IF $value IS AN ARRAY, MAKE IT A CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // IF THE CUSTOM FIELD ALREADY HAS A VALUE
update_post_meta($post->ID, $key, $value);
} else { // AND IF IT DOESN'T HAVE A VALUE
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // IF IT'S BLANK, DELETE
}
}
add_action('save_post', 'voodoo_save_meta', 1, 2); // SAVE FUNCTION FOR ALL CUSTOM FIELDS
You should be able to cruise through that code and pick up the changes. And see the patterns. For instance our first function from before is still here. We just use add_meta_box 3 times. The first one only shows up on a Custom Post Type of ve_members, the second and third are for posts. Each of the three have callbacks to their own uniques function. The first one goes to the sidebar in the post screen, the second and third go to ‘normal’ which is under the post edit box. You can ignore lines 8 and 9, as those are completely theme specific. I just use that to only include the 3rd iteration of add_meta_box if a custom admin panel for my theme has a certain option checked “Yes”. Unless you have this same option in your theme, it wouldn’t apply.
Then we have 3 functions in a row, ve_social_links, ve_titleurl_links and ve_post_honey. Each of these refers to one of the functions we set up in each of the add meta boxes. Each one follows the layout we had in the first example. And you’ll notice that we use the same noncename for all three. We can just use the same authentication for all three, no need to build new ones. We establish all the variables and custom fields we need for each function. After that we loop through all of the variables and get them in an array. Just a list of all the variables from all three functions. And finally we have our save routine, which remains unchanged. It covers all three functions.
You may find that last block of code overwhelming, I didn’t want to confuse you too much. And my explanations may not be the greatest. But if you read the first part, and look at the simpler code we started with, and then compare it to that last much bigger block of code, I’m hoping you can pick out the patterns. Then you can start building your own and see the different ways to use this.
So hopefully that helped a little. Go play around with adding the meta boxes to your theme. This process works the same for a parent or child theme so nothing you need to worry about for that! Of course I always like to mention, you should always make a child theme.

This is a great article, thanks so much. I’ve read a few tutorials about metaboxes, but yours is the one I’m pinching code from.
Can I ask why the line:
if( $post->post_type == 'revision' ) return; // WE DON'T WANT IT STORED TWICEis in the foreach loop in the save routine? Surely if the post type is “revision”, this will be true for all the variables, so why check more than once?
Also, in the same loop, why delete the metadata if the new value is blank? The previous lines would have added or updated the metadata with the blank value.
Thanks for taking the time to comment, I appreciate the positive feedback. I’ll tell ya what, I’m no coder. I lift things from all over the place, and test it a bunch and if it’s good, use it and share. So I probably lifted the save routine from somewhere, or built it from a couple other people’s examples. It worked perfectly for me, so I left it as I have it now. But if I have unnecessary stuff in there that’s good to know. Just leave it out if it’s not needed. I’ll try to do some further testing and report back soon. I update my posts on here constantly if I find better or neater ways to do things.
How am i gonna save looping fields from java/jquery?
i can’t find solutions… help please :s
<script type="text/javascript"> function add() { var count = 1; for(var i=0; i<count; i++) { var input = document.createElement('INPUT') input.name='prod_color[]' input.style.width='80px' input.style.padding='5px' input.style.textAlign='center' input.style.height='30px' input.style.marginRight='4px' input.value='<?php $prod_color ?>' // bind jscolor var col = new jscolor.color(input) document.getElementById('ColorBoxes').appendChild(input); } }foreach ($_POST['color'] as $prod_color) { // saving colors $catalog_meta['color'] = $prod_color; }i can’t save the fields… :’s help please..
Pingback: Melhor alternativa ao Meta Box, o campo personalizado puro - Wordpress » Snippets