Custom Post Types and Metaboxes - MyWay
I am not a professional writer so apologies for my incoherent rambling!
UPDATE I am aware of some issues in this implementation. Specifically with my modifications to allow file and image uploads. As stated this is not necessarily the right way to do it but it works for my situation. Since writing this I have found this https://github.com/rilwis/meta-box a great plugin and piece of code you can use. Check it out it has certainly sped up my development.
WordPress has always been one of the very best for bloggers and over the last couple of years has also proved itself to be a robust and stable CMS. WordPress 3.0 has grown up quite a lot and has given (or improved) on some features to really let developers make more of their WordPress CMS. Let’s dig into them!
One of the greatest features is Custom Post Types. WordPress by default has 2 types of content (I know there are more if you count links etc but this is a simple guide!) posts and pages. Sometimes, however, you want to create a new post type that has its own options, its own templates and its own taxonomy. This is where Custom Post Types come in. Now I am no WordPress expert, I use it, I use it a lot but there are some things that I have never needed to do. Until now!
A Custom Post Type basically allows you to have your own content section for example, Case Studies. You may want to capture information in a specific way to better fit into your template.
We need to do 2 things:
- Create our Custom Post Type
- Add Custom Meta boxes to that post type
I have searched high and low to find the ultimate tutorial/guide on how to do this and there are loads out there a few that personally caught my eye! Simply Googling brings page hundreds of thousands of results. Anyway here is how I do it. I’m not saying it is the right way but it works for me!
Create the Custom Post Type
This is achieved using the register_custom_post function built into WordPress. You can read and read and read or we can just do? I got started by using the Custom Post Type Code Generator, it is a fantastic tool written by Themergency. Simple fill in the magic boxes and it generates some really nice useful code. Here’s mine for our case study example! This goes in our themes functions.php (if you don’t have one don’t worry just create one!)
/////////////////
// Case Study //
///////////////
// add post type
add_action( 'init', 'register_cpt_case_study' ); //init our function
function register_cpt_case_study() { // our function should obviously match what your initing above
$labels = array( //set some key labels
'name' => _x( 'Case Studies', 'case_study' ),
'singular_name' => _x( 'Case Study', 'case_study' ),
'add_new' => _x( 'Add New', 'case_study' ),
'add_new_item' => _x( 'Add New Case Study', 'case_study' ),
'edit_item' => _x( 'Edit Case Study', 'case_study' ),
'new_item' => _x( 'New Case Study', 'case_study' ),
'view_item' => _x( 'View Case Study', 'case_study' ),
'search_items' => _x( 'Search Case Studies', 'case_study' ),
'not_found' => _x( 'No case studies found', 'case_study' ),
'not_found_in_trash' => _x( 'No case studies found in Trash', 'case_study' ),
'parent_item_colon' => _x( 'Parent Case Study:', 'case_study' ),
'menu_name' => _x( 'Case Studies', 'case_study' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'supports' => array( 'title', 'editor' ),
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 20,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => array('slug' => 'case-studies')
);
register_post_type( 'case_study', $args );
}
I’m not going to go into too much detail as it is fairly straight forward. We define our labels which deals with the Name and some specific labels you will find around the WordPress Admin menu. We then define our options for the custom post type. Bringing in our label variables then some settings. You can get full info from the codex page but here is the low down!
- Hierarchical - false is like posts, true is like pages. If set to true then a post can be the child of another. I normally just set to false!
- Supports - this defines which of the WordPress default fields to show (e.g.. title, author, thumbnail, revisions, comments etc) If you leave this out it will just show the title and editor.
- Public - basically sets the state of a few options like publicly queryable, show UI, show in menu etc (which obviously you can override)
- Show UI, Show in Menu and Menu Position - these all deal with how the post type is rendered and displayed within the admin area. Menu position allows you to change where in the left hand menu your “box” appears!
- Show in Nav Menus - this is quite a good one, this when set to true will show the Posts in their own box on the WordPress menu screen allowing you to easily pick which entries you want to show on your navigation menu.
The others are named fairly well so you can work out what they do. The rewrite option is worth mentioning though. By default WordPress will enable friendly URLs so we would get a URL based on the Post Type Name in our case /case_study/our-case-study. By specifying the slug key I am choosing to use /case-studies/our-case-study instead. There is loads of tutorials online if you want to do anything crazier than this with custom taxonomies etc for now though I’m moving on.
So your custom post type is now created! If you goto your Admin Dashboard you should see it on the left menu somewhere (depending on what you set menu_position to)
You can create custom template files for your custom post types allowing them to be different to your normal single.php or archives.php simply create your template and name it single-my_post_type.php or archive-my_post_type.php so in our example I have single-case_study.php and archive-case_study.php. You can also use all kinds of variations in WP-Query to pull your custom post type data elsewhere on your site if you need to. That’s a little outside the scope of this example though!
Crazy Custom Meta Boxes
Now this is where the fun starts, and where I had most of the problems! Again I am going to show you how I do it! If you find a better way then happy days!
Now I’m using a special extra class file to do the hard work! You can find the original tutorial here and his GitHub project for it here. I have also forked his project and added some new things like image and file uploads so you can include those in your meta boxes! You can check out my version here. PLEASE NOTE MINE ISN’T EXACTLY PRODUCTION READY! IT WORKS FOR MY OWN SITUATION BUT MAY NEED MODIFYING FOR YOUR ENVIRONMENT OR USE. SEE THE UPDATED BLOG ENTRY FOR A MORE COMPLETE CLASS YOU CAN USE!
So here we go how do we use it! Firstly download the files from GitHub and extract. You will get a stupid folder name with a few files inside (I am going to use MY OWN version from GitHub in this example!). In your theme folder create a folder called includes and upload the included files to them you should end with something like the image.
Now back to our theme functions.php
So we’ve uploaded the cool new class to handle our meta boxes we now need to include it in our theme.
require_once TEMPLATEPATH .'/includes/SmartMetaBox.php';
Now we can start adding our meta boxes!
// meta box shiz
add_smart_meta_box('case_study_box', array(
'title' => 'Case Study Options', //title of meta box
'pages' => array('case_study'), // where do we show it?
'context' => 'normal', //positioning of box
'priority' => 'high', //how important is this really?
'fields' => array(
array(
'name' => 'Customer Name',
'id' => 'customer_name',
'desc' => 'Please enter the customer name as it should be displayed on the case study page',
'type' => 'text',
),
array(
'name' => 'Customer Logo',
'id' => 'customer_logo',
'desc' => 'Please add a customer logo. Logos should be scaled to a maximum of 150px x 150px',
'type' => 'image',
),
array(
'name' => 'Case Study Download',
'id' => 'case_study_download',
'desc' => 'Please add a PDF of the case study for download',
'type' => 'file',
)
//more arrays for additional fields can go here
)
));
Ok so that’s the code now let’s find out what it’s doing! The add_smart_meta_box calls our new shiny class to handle the actually building of the meta boxes and we give it a unique name in our case case_study_box.
First we set up our box by specifying the Title of the box, which pages it should show on (yes it’s an array so you can specify multiple!). Now context determines it’s location on the page. Normal will put it below the editor, side will put it, you guessed it in the right sidebar and advanced…well I couldn’t really tell you! Priority will then determines it’s place in relation to other elements. High will put it just below the editor window above excerpt, revisions etc where as low would stick it below any of those (if you’ve included them in your custom post type supports key).
Then onto the fields array in the example you can see 3 fields have been set up. You can of course add multiple! kinda self explanatory! You can have text, textarea, image, file, radio, select, checkbox you can check the docs on the class on how to use them all!
Now if you’ve been following along and you goto Add Case Study in your admin area you will see our new meta box and 3 fields! How awesome is that!
The full code from my functions.php is below.
<?php
/*
//Custom post types
*/
//You need to include this file to handle the custom meta box generation!
require_once TEMPLATEPATH .'/includes/SmartMetaBox.php';
/////////////////
// Case Study //
///////////////
// add post type
add_action( 'init', 'register_cpt_case_study' );
function register_cpt_case_study() {
$labels = array(
'name' => _x( 'Case Studies', 'case_study' ),
'singular_name' => _x( 'Case Study', 'case_study' ),
'add_new' => _x( 'Add New', 'case_study' ),
'add_new_item' => _x( 'Add New Case Study', 'case_study' ),
'edit_item' => _x( 'Edit Case Study', 'case_study' ),
'new_item' => _x( 'New Case Study', 'case_study' ),
'view_item' => _x( 'View Case Study', 'case_study' ),
'search_items' => _x( 'Search Case Studies', 'case_study' ),
'not_found' => _x( 'No case studies found', 'case_study' ),
'not_found_in_trash' => _x( 'No case studies found in Trash', 'case_study' ),
'parent_item_colon' => _x( 'Parent Case Study:', 'case_study' ),
'menu_name' => _x( 'Case Studies', 'case_study' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'supports' => array( 'title', 'editor' ),
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 20,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => array('slug' => 'case-studies')
);
register_post_type( 'case_study', $args );
}
// meta box shiz
add_smart_meta_box('case_study_box', array(
'title' => 'Case Study Options', //title of meta box
'pages' => array('case_study'), // where do we show it?
'context' => 'normal', //positioning of box
'priority' => 'high', //how important is this really?
'fields' => array(
array(
'name' => 'Customer Name',
'id' => 'customer_name',
'desc' => 'Please enter the customer name as it should be displayed on the case study page',
'type' => 'text',
),
array(
'name' => 'Customer Logo',
'id' => 'customer_logo',
'desc' => 'Please add a customer logo. Logos should be scaled to a maximum of 150px x 150px',
'type' => 'image',
),
array(
'name' => 'Case Study Download',
'id' => 'case_study_download',
'desc' => 'Please add a PDF of the case study for download',
'type' => 'file',
)
//more arrays for additional fields can go here
)
));
?>
The only thing left to know is how you call certain data from those new fields! This is again handled by our shiny class! Simply use
SmartMetaBox::get('field-id')
This is how I have used it in my single-case_study.php example.
<?php get_header(); ?>
<div id="content">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post();?>
<h2><a href="<?php the_permalink();?>"><?php the_title();?></a></h2>
<h3>Customer: <?php echo SmartMetaBox::get('customer_name');?></h3>
<img src="<?php echo SmartMetaBox::get('test_image');?>"/>
<a href="<?php echo SmartMetaBox::get('test_file');?>"><?php echo SmartMetaBox::get('customer_name');?> Case Study Download</a>
<p><?php the_content();?></p>
<?php endwhile; ?>
<?php else : ?>
<p>No posts found</p>
<?php endif; ?>
</div>
<?php get_sidebar(); ?>
<?php get_footer(); ?>
You can see we are pulling from all the fields in this example!
That’s essentially it from me! I’ve had trouble doing this and now I’ve finally found a solution that works for me without having to resort to plugins I’m happy. There are of course limitations in place especially when it comes to multiple uploads fields in the meta boxes all of which have been discussed on the original blog/tutorial for the class we have forked and used.
I hope this turns out to be useful for someone!
Filed under Developer. No comments, on purpose.

