I’m writing this post because this really confused me at first. There are some other examples online that maybe, if you’re here, you’ve already found:

http://www.farinspace.com/how-to-create-custom-wordpress-meta-box/

http://shibashake.com/wordpress-theme/add-metabox-custom-post-type

http://codex.wordpress.org/Function_Reference/add_meta_box

I think the main reason I was having trouble wrapping my head around it all was because the samples use so many similar names for each part I wasn’t sure what was referring to the post_type, the database field, the input variable, or the input name.  Hopefully I can clear it up for anyone else who’s also struggling with implementing their own Meta Boxes.  Also the samples seem to assume you’re only adding ONE custom meta box, but I wanted to add three.  My specific application is to add these to the “edit.php” page for a custom post type, but the principles would also work if you were adding custom meta boxes to the regular post or page editors too.

I’m going to assume you’ve already set up your Custom Post_Type, and for my examples I’ll be using a custom post_type of “magazine.”  Now I will concentrate on just the Meta Box part.  First we need to create a function for each of our Meta Boxes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function my_first_metabox($post) {
/* Gets any existing '_myfirst' meta box content */
$my_first = get_post_meta($post->ID,  '_myfirst', TRUE);
/* If there is none our variable is set to empty quotes */
if (!$my_first) $my_first = '';
/* Create the security nonce hidden input field */
wp_nonce_field( 'magazine'.$post->ID, 'my_first_noncename');
/* Now for our actual input box.  I'm using a textarea, but this could be
whatever HTML input element you want to use and echo any existing
content into it.*/

?>

<textarea id="my_first" name="my_first" cols="40" rows="5">
<?php echo $my_first; ?>
</textarea>

<?php } ?>

Then I create two more functions, where the only things I change up are “first” for “second” and then “third.”  You’ll notice that my meta content name in the example above is “_myfirst” rather than “myfirst” (like you’d probably do with meta in a Custom Field).  I believe opening with an underscore was recommended for post meta data because some databases are prefixed with “ci” and if you fire up PHPadmin and look at the post_meta tables in the database you’ll notice all the built-in meta entries start with an underscore, so at the very least we’ll be consistent.  So, if you’re following along our functions should now look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function my_first_metabox($post) {
$my_first = get_post_meta($post->ID,  '_myfirst', TRUE);
if (!$my_first) $my_first = '';
wp_nonce_field( 'magazine'.$post->ID, 'my_first_noncename');?>
<textarea id="my_first" name="my_first" cols="40" rows="5"><?php echo $my_first; ?></textarea>
}

function my_second_metabox($post) {
$my_second = get_post_meta($post->ID,  '_mysecond', TRUE);
if (!$my_second) $my_second = '';
wp_nonce_field( 'magazine'.$post->ID, 'my_second_noncename');?>
<textarea id="my_second" name="my_first" cols="40" rows="5"><?php echo $my_second; ?></textarea>
}

function my_third_metabox($post) {
$my_third = get_post_meta($post->ID,  '_mythird', TRUE);
if (!$my_third) $my_third = '';
wp_nonce_field( 'magazine'.$post->ID, 'my_third_noncename');?>
<textarea id="my_third" name="my_third" cols="40" rows="5"><?php echo $my_third; ?></textarea>
<?php } ?>

Those will only allow users to input the data.  We need to be able to collect it, authenticate it, check user permissions, and write the data into our database with the post.  We do that with our “save” function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
function save_metabox_data($post_id) {
/* check if this is an auto save.  If it is the form hasn't been submitted so bail early */
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
/* verify that this came from the post edit page with proper authentication
and  if any one of these fails the test bail and do nothing */

if ( !wp_verify_nonce( $_POST['my_first_noncename'], 'magazine'.$post_id) || !wp_verify_nonce( $_POST['my_second_noncename'], 'magazine'.$post_id ) || !wp_verify_nonce( $_POST['my_third_noncename'], 'magazine'.$post_id) ) {
return $post_id;
}
/* Check the User Permissions for editing posts.  If not, we bail */
if ( !current_user_can( 'edit_post', $post_id ) )
return $post_id;
/* If we got this far everything Authenticated so let's find and save some data */
/* First lets get our post and put it into a variable */
$post = get_post($post_id);
/* Now check to make sure it is of our custom post_type */
if ($post->post_type == 'magazine') {
/* Now we actually update the post meta data.  If the field does not already exist
it automatically calls "add_post_meta" instead.  The first parameter is the post_id
the second is the actual name of our meta field in the database with leading underscore,
the last parameter is referencing the content of our input field in HTML by name */

update_post_meta($post_id, '_myfirst', $_POST['my_first']);
update_post_meta($post_id, '_mysecond', $_POST['my_second']);
update_post_meta($post_id,'_mythird', $_POST['my_third']);
}
return $post_id;
}
?>

However, if you’ve been hopping back to look at your post editing page you’ve yet to see anything change.  That’s because we haven’t actually fired these functions off yet!  We need to do the typical WordPress thing of functions calling functions:

1
2
3
4
5
6
7
8
9
10
<?php
function my_custom_meta_boxes() {
/* First parameter is just a name.  I used "_section" just to keep it clear that they are just arbitrary section names
which don't seem to actually get used that I can see.  The next part is whatever title you want at the top of the
Meta Box.  Third parameter is our custom post_type name.  Fourth is the "position" of the meta box, and lastly
is the "priority" of how high up on the page it should be ("core" is right after the regular edit box). */

add_meta_box( 'myfirst_section',  __('My First Title'), 'my_first_metabox', 'magazine','normal','core');
add_meta_box('mysecond_section', __('My 2nd Title'), 'my_second_metabox', 'magazine','normal','core');
add_meta_box('mythird_section', __('My Third Title'), 'my_third_metabox', 'magazine','normal','core');
} ?>

In case you’re wondering your options for “position” are “normal,” “advanced,” or “side” and the “priority” options are “high,” “core,” “default,” and “low” (note that none of these will position the meta box above the regular editing window).  Also, if you were doing this for the regular post or page editors you’d just change the custom post_type name to “post” or “page” respectively.

Now we just need to do two last little things.  We need to run that function above, which in turn runs all the functions for the meta boxes injecting them into the page, and then we need to set up a trigger for the save function:

1
2
3
4
<?php
add_action('admin_init', 'my_custom_meta_boxes' );
add_action('save_post',' save_metabox_data' );
?>

That’s it!  Three custom meta boxes appended to the edit post just below the regular editing window (if your Custom Post_Type supported it), and ONLY appearing on the custom post_type “magazine” editing page.

I went on to add the TinyMCE Rich Text Editor to the Meta Boxes with functioning “Visual” and “HTML” edito mode tabs.  That code is in another post: http://www.kmhcreative.com/labs/2012/02/15/wordpress-meta-boxes-rich-text-mode-tabs/