Site icon WP Smith

Custom Post Types and Using Meta Boxes

Understanding WordPress Custom Post Types

First, if there is any confusion, WordPress refers to metaboxes as one word (see add_meta_box and remove_meta_box). The confusion rises because of the function name which separates the words. While I am not going to speculate as to the right way, as far as I am concerned, it doesn't matter. But that may be because I don't have a dog in the fight or discussion. So I will defer to better and smarter people. These same zealots are the ones who argue for the proper spelling of WordPress not extending grace at times, even when a person who knows better may mistakenly type something different. Again, to me, while spelling WordPress correctly matters from a branding perspective, from a developer's perspective where everything is lower case or camel case, again it doesn't matter (esp since all WordPress functions abbreviate WordPress to wp_). Now on to more pressing things (function off_soap_box() {break;}).

There are several posts on how to incorporate custom fields and metaboxes into your custom post type. To me this is the most difficult part of custom post types. You can also find a plethora of examples and tutorials of custom metaboxes for your custom post type that use simple text fields or text areas (e.g., a good video metabox example by Andrew Dodson, a good example here by function, new2wp for a simple metabox for links & link descriptions, Re-cycled Air, .net Magazine for Tumblr-like metabox for quotes with post formats), as those are the most popular. However, finding good tutorials on the use of checkboxes or radio buttons are a bit more difficult to find. Jeremiah Tolbert gives a good tutorial on a dropdown selection metabox.

Two notable examples are Curtis Henson's Links custom post type using a PHP Class and WPTheming's Events metaboxes, good thorough tutorial.

However, I need to clarify something that I hear a lot. When you register your custom post type, you have the opportunity to use an argument called "register_meta_box_cb." However, you do not have to use that argument. Using it allows you to cut a couple steps, but if you are using free custom post type plugins then they probably do not offer this ability.

To add a metabox to a custom post type,
[php]<?php
// Add the Metabox for CPT

add_action( 'add_meta_boxes', 'add_cpt_metabox', 10 ); //note, it has to be add_meta_boxes hook for WP 3.0+
function add_cpt_metabox() {
add_meta_box('cpt-metabox', 'Metabox TITLE', 'prefix_cpt_metabox_function', 'my_cpt_registered_name', 'normal', 'default');
}

// The CPT Metabox
function prefix_cpt_metabox_function() {
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="cpt_metabox_noncename" id="cpt_metabox_noncename" value="' . wp_create_nonce( plugin_basename(__FILE__) ) . '" />';

// Echo metabox body stuff (fields/text, etc)
}

/* Do something with the data entered */
add_action( 'save_post', 'prefix_cpt_metabox_save_postdata' );
function prefix_cpt_metabox_save_postdata( $post_id ) {
// verify if this is an auto save routine.
// If it is our form has not been submitted, so we dont want to do anything
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;

// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times

if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename( __FILE__ ) ) )
return;

// Check permissions
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return;
}

// OK, we're authenticated: we need to find and save the data

$mydata = $_POST['myplugin_new_field'];

// Do something with $mydata
// probably using add_post_meta(), update_post_meta(), or
// a custom table (see Further Reading section below)

return $mydata;
}
[/php]

For a good alternative for saving, see Nathan Rice's code: Adapted version presented by WP Theming's Post, How to Add a Metabox to a Custom Post Type.

These examples provide good hard coding examples, so I wanted to discuss two other possibilities.
First is a plugin: More Fields, which works perfectly with More Types and More Taxonomies.

Click for Larger Image

Second and a much better choice in my option as it is extremely flexible is the Custom Metaboxes and Fields for WordPress script by Andrew Norcross (@norcross), Jared Atchison (@jaredatch), and Bill Erickson (@billerickson). Bill Erickson has written a brief example here with an excellent subsequent discussion in the comments area.

However, here is an example that I have used recently with a restaurant theme and a menu item post type.

Here’s how to set up your own metaboxes:

  1. Download a copy of our metabox code from GitHub
  2. Create a “lib” directory in your theme or child theme, and a “metabox” directory inside “lib”. Upload the files you downloaded above into /wp-content/themes/your-theme/lib/metabox
  3. Include '/lib/metabox/init.php' in your functions.php file, and define your metaboxes in an array (see below). For Genesis users, it's require_once( CHILD_DIR . '/lib/metabox/init.php' );; for others, try require_once( STYLESHEETPATH . '/lib/metabox/init.php' );

Each metabox is stored as an array. There’s a ton of field options, including text, textarea, checkbox, dropdown list, and WYSIWYG. For examples on how to use all the fields, look at /metabox/example-functions.php.

First step is that I registered my custom post type.

[php]<?php add_action('init', 'dp_cpt_init');
function dp_cpt_init() {
$labels= array( 'name' =--> _x('Menu Items', 'post type general name'),
'singular_name' => _x('Menu Item', 'post type singular name'),
'add_new' => _x('Add New', 'menu_item'),
'add_new_item' => __('Add New Menu Item'),
'edit_item' => __('Edit Menu Item'),
'new_item' => __('New Menu Item'),
'view_item' => __('View Menu Item'),
'search_items' => __('Search Menu Item'),
'not_found' => __('No menu items found'),
'not_found_in_trash' => __('No menu items found in Trash'),
'parent_item_colon' => '',
'menu_name' => 'Menu Items'
);

$args = array(
'labels' => $labels,
'description' => 'Menu items for various menus.',
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'menu-item'),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'menu_icon' => CHILD_URL .'/lib/cpt/images/menu_item_cpt_16x16.png',
'supports' => array('title','editor','revisions','thumbnail'),
'taxonomies' => array('dp_menu','dp_toppings')
);
register_post_type( 'dp_menu_items' , $args );

}

add_action( 'init', 'dp_create_custom_taxonomies', 0 );
function dp_create_custom_taxonomies() {
//Register Taxonomies
register_taxonomy('dp_menu',array (0 => 'dp_menu_items'),array( 'hierarchical' => true, 'label' => 'Menus','show_ui' => true,'query_var' => true,'rewrite' => array('slug' => 'menus'),'singular_label' => 'Menu') );
register_taxonomy('dp_toppings',array (0 => 'dp_menu_items'),array( 'hierarchical' => false, 'label' => 'Toppings','show_ui' => true,'query_var' => true,'rewrite' => array('slug' => 'toppings'),'singular_label' => 'Topping') );
}
[/php]

Second step is to follow Bill's steps, and add the following below the previous content.

[php]require_once( CHILD_DIR . '/lib/metabox/init.php' );
// Include & setup custom metabox and fields
$prefix = 'dp_';
$meta_boxes = array();
global $price_info;

$meta_boxes[] = array(
'id' => 'dp_menu_item_info',
'title' => 'Menu Item Price Information',
'pages' => array('dp_menu_items'), // post type
'context' => 'side',
'priority' => 'high',
'show_names' => true, // Show field names on the left
'fields' => array(
array(
'name' => 'Small Pizza Price',
'desc' => 'Small Pizza',
'id' => $prefix . 'price_textmoney', // dp_price_textmoney
'type' => 'text_money'
),
array(
'name' => 'Medium Pizza Price',
'desc' => 'Medium Pizza',
'id' => $prefix . 'price1_textmoney', // dp_price1_textmoney
'type' => 'text_money'
),
array(
'name' => 'Large Pizza Price',
'desc' => 'Large Pizza',
'id' => $prefix . 'price2_textmoney', // dp_price2_textmoney
'type' => 'text_money'
),
array(
'name' => 'Extra Large Pizza Price',
'desc' => 'XL Pizza',
'id' => $prefix . 'price3_textmoney', // dp_price3_textmoney
'type' => 'text_money'
),
array(
'name' => 'Sale Price',
'desc' => 'Sale Campaign Price',
'id' => $prefix . 'price4_textmoney', // dp_price4_textmoney
'type' => 'text_money'
)
)
);
[/php]
And here is an example of the outcome: