Recently, someone contacted me to develop a custom post type setup for their website, and they were lamenting how there is nothing on the web that succinctly walks them through the process: soup to nuts. So here's my attempt! However, I am going to assume basic knowledge of custom post types. If you haven't read through my Understanding Custom Post Types series yet, you may want to do that.
First, I created a new php file called wps-admin-functions.php. This is where we will house our Custom Post Types, Taxonomies, and Metaboxes. We could have added this to our theme’s function.php file but this will allow us to have a separate file dedicated to our newly added features. To do this, simply create a new text document in your favorite text editor and save as wps-admin-functions.php.
1. Register your Custom Post Type
[php]<?php
// registration code for discbrakes post type
function wps_register_discbrakes_posttype() {
$labels = array(
'name' => _x( 'Disc Brakes', 'post type general name' ),
'singular_name' => _x( 'Disc Brake', 'post type singular name' ),
'add_new' => _x( 'Add New', 'Disc Brake'),
'add_new_item' => __( 'Add New Disc Brake '),
'edit_item' => __( 'Edit Disc Brake '),
'new_item' => __( 'New Disc Brake '),
'view_item' => __( 'View Disc Brake '),
'search_items' => __( 'Search Disc Brakes '),
'not_found' => __( 'No Disc Brake found' ),
'not_found_in_trash' => __( 'No Disc Brakes found in Trash' ),
'parent_item_colon' => ''
);
$supports = array( 'title' , 'editor' , 'thumbnail' , 'excerpt' , 'revisions' );
$post_type_args = array(
'labels' => $labels,
'singular_label' => __( 'Disc Brake' ),
'public' => true,
'show_ui' => true,
'publicly_queryable' => true,
'query_var' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'discbrakes' ),
'supports' => $supports,
'menu_position' => 5,
'taxonomies' => array( 'wps_axlesizes' ),
'menu_icon' => 'http://mydomain.com/wp-content/themes/lib/images/discbrakes-icon.png'
);
register_post_type( 'wps_discbrakes' , $post_type_args );
}
add_action( 'init', 'wps_register_discbrakes_posttype' );
[/php]
Notice in this code that I associate my custom post type with my coming custom taxonomy: 'taxonomies' => array( 'wps_axlesizes' ),
. Also, note that I am using a prefix on both my custom post type and taxonomy. The reason this is done is that there may be a naming conflict with a plugin or other code in your theme. The prefix uniquely identifies it to prevent this. It is a best practice, and make sure you do not use the wp_ or genesis_ prefixes.
2. Register your Custom Taxonomy
[php]<?php
// registration code for AxleSizes taxonomy
function wps_register_axlesizes_tax() {
$labels = array(
'name' => _x( 'Axle Sizes', 'taxonomy general name' ),
'singular_name' => _x( 'Axle Size', 'taxonomy singular name' ),
'add_new' => _x( 'Add New Axle Size', 'Axle Size'),
'add_new_item' => __( 'Add New Axle Size' ),
'edit_item' => __( 'Edit Axle Size' ),
'new_item' => __( 'New Axle Size' ),
'view_item' => __( 'View Axle Size' ),
'search_items' => __( 'Search Axle Sizes' ),
'not_found' => __( 'No Axle Size found' ),
'not_found_in_trash' => __( 'No Axle Size found in Trash' ),
);
$pages = array( 'wps_discbrakes' );
$args = array(
'labels' => $labels,
'singular_label' => __( 'Axle Size' ),
'public' => true,
'show_ui' => true,
'hierarchical' => false,
'show_tagcloud' => false,
'show_in_nav_menus' => true,
'rewrite' => array('slug' => 'axle-sizes'),
);
register_taxonomy( 'wps_axlesizes' , $pages , $args );
}
add_action( 'init' , 'wps_register_axlesizes_tax' );[/php]
Notice in the registration, the taxonomy is associated with my custom post type: $pages = array( 'wps_discbrakes' );
.
Once these are registered, you should see your custom post type to the left under POST.
3. Create my Metaboxes
With metaboxes, you can go the long way; however, I prefer to use a class. Soon WordPress will have a metabox class, hopefully in WordPress 3.3, so this section will be rendered "obsolete" yet still usable. First, download the custom metabox code from GitHub. For a great explanation of the code, see Bill Erickson's tutorial. Because of the tutorial, I won't break down this section.
Once downloaded, place the metabox folder in your lib folder in the child theme folder so that: CHILD_URL . '/lib/metabox/init.php'
So the file structure is
CHILD-THEME-FOLDER
- IMAGES FOLDER
- LIB FOLDER
- METABOX FOLDER
- IMAGES FOLDER
- wps-admin-functions.php
- functions.php
- style.css
- readme.txt
[php]<?php
// Create Metaboxes
$prefix = '_wps_';
add_filter( 'cmb_meta_boxes', 'wps_create_metaboxes' );
function wps_create_metaboxes() {
global $prefix;
$meta_boxes[] = array(
'id' => 'disk-brakes-info',
'title' => 'Disk Brakes Information',
'pages' => array( 'wps_discbrakes' ), // post type
'context' => 'normal',
'priority' => 'low',
'show_names' => true, // Show field names left of input
'fields' => array(
array(
'name' => 'Instructions',
'desc' => 'In the right column upload a featured image. Make sure this image is at least 900x360px wide. Then fill out the information below.',
'id' => $prefix . 'title',
'type' => 'title',
),
array(
'name' => 'Part Number',
'desc' => 'Enter the part number (e.g., XXXXXXX)',
'id' => $prefix . 'part_number',
'type' => 'text'
),
array(
'name' => 'Rotor',
'desc' => '',
'id' => $prefix . 'rotor',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Caliper',
'desc' => '',
'id' => $prefix . 'caliper',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Mounting Bracket',
'desc' => '',
'id' => $prefix . 'mounting_bracket',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Weight per Axle Set',
'desc' => 'e.g., 45 LBS.',
'id' => $prefix . 'weight_per_axle_set',
'type' => 'text'
),
),
);
return $meta_boxes;
}
// Initialize the metabox class
add_action( 'init', 'be_initialize_cmb_meta_boxes', 9999 );
function be_initialize_cmb_meta_boxes() {
if ( !class_exists( 'cmb_Meta_Box' ) ) {
require_once(CHILD_DIR . '/lib/metabox/init.php');
}
}
[/php]
This will create the metaboxes for the custom post type.
4. Clear Permalinks
Now in your admin area, navigate to Settings > Permalinks and click Save Changes. This will flush and clear your permalinks. This is important if you are getting strange 404 Page Not Found errors.
So our finished product is this: wps-admin-functions.php
[php]
<?php
/**
* Admin Functions
*
* This file is responsible for registering the
* custom post types, taxonomies, and metaboxes.
*
* @author Travis Smith <[email protected]>
* @copyright Copyright (c) 2011, Travis Smith
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*
*/
// registration code for discbrakes post type
// registration code for discbrakes post type
function wps_register_discbrakes_posttype() {
$labels = array(
'name' => _x( 'Disc Brakes', 'post type general name' ),
'singular_name' => _x( 'Disc Brake', 'post type singular name' ),
'add_new' => _x( 'Add New', 'Disc Brake'),
'add_new_item' => __( 'Add New Disc Brake '),
'edit_item' => __( 'Edit Disc Brake '),
'new_item' => __( 'New Disc Brake '),
'view_item' => __( 'View Disc Brake '),
'search_items' => __( 'Search Disc Brakes '),
'not_found' => __( 'No Disc Brake found' ),
'not_found_in_trash' => __( 'No Disc Brakes found in Trash' ),
'parent_item_colon' => ''
);
$supports = array( 'title' , 'editor' , 'thumbnail' , 'excerpt' , 'revisions' );
$post_type_args = array(
'labels' => $labels,
'singular_label' => __( 'Disc Brake' ),
'public' => true,
'show_ui' => true,
'publicly_queryable' => true,
'query_var' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'discbrakes' ),
'supports' => $supports,
'menu_position' => 5,
'taxonomies' => array( 'wps_axlesizes' ),
'menu_icon' => 'http://mydomain.com/wp-content/themes/lib/images/discbrakes-icon.png'
);
register_post_type( 'wps_discbrakes' , $post_type_args );
}
add_action( 'init', 'wps_register_discbrakes_posttype' );
// registration code for AxleSizes taxonomy
function wps_register_axlesizes_tax() {
$labels = array(
'name' => _x( 'Axle Sizes', 'taxonomy general name' ),
'singular_name' => _x( 'Axle Size', 'taxonomy singular name' ),
'add_new' => _x( 'Add New Axle Size', 'Axle Size'),
'add_new_item' => __( 'Add New Axle Size' ),
'edit_item' => __( 'Edit Axle Size' ),
'new_item' => __( 'New Axle Size' ),
'view_item' => __( 'View Axle Size' ),
'search_items' => __( 'Search Axle Sizes' ),
'not_found' => __( 'No Axle Size found' ),
'not_found_in_trash' => __( 'No Axle Size found in Trash' ),
);
$pages = array( 'wps_discbrakes' );
$args = array(
'labels' => $labels,
'singular_label' => __( 'Axle Size' ),
'public' => true,
'show_ui' => true,
'hierarchical' => false,
'show_tagcloud' => false,
'show_in_nav_menus' => true,
'rewrite' => array('slug' => 'axle-sizes'),
);
register_taxonomy( 'wps_axlesizes' , $pages , $args );
}
add_action( 'init' , 'wps_register_axlesizes_tax' );
// Create Metaboxes
$prefix = '_wps_';
add_filter( 'cmb_meta_boxes', 'wps_create_metaboxes' );
function wps_create_metaboxes() {
global $prefix;
$meta_boxes[] = array(
'id' => 'disk-brakes-info',
'title' => 'Disk Brakes Information',
'pages' => array( 'wps_discbrakes' ), // post type
'context' => 'normal',
'priority' => 'low',
'show_names' => true, // Show field names left of input
'fields' => array(
array(
'name' => 'Instructions',
'desc' => 'In the right column upload a featured image. Make sure this image is at least 900x360px wide. Then fill out the information below.',
'id' => $prefix . 'title',
'type' => 'title',
),
array(
'name' => 'Part Number',
'desc' => 'Enter the part number (e.g., XXXXXXX)',
'id' => $prefix . 'part_number',
'type' => 'text'
),
array(
'name' => 'Rotor',
'desc' => '',
'id' => $prefix . 'rotor',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Caliper',
'desc' => '',
'id' => $prefix . 'caliper',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Mounting Bracket',
'desc' => '',
'id' => $prefix . 'mounting_bracket',
'type' => 'select',
'options' => array(
array('name' => 'Stainless Steel', 'value' => 'stainless-steel'),
array('name' => 'E-Coat (Black)', 'value' => 'e-coat-black'),
array('name' => 'Option Three', 'value' => 'none')
)
),
array(
'name' => 'Weight per Axle Set',
'desc' => 'e.g., 45 LBS.',
'id' => $prefix . 'weight_per_axle_set',
'type' => 'text'
),
),
);
return $meta_boxes;
}
// Initialize the metabox class
add_action( 'init', 'wps_initialize_cmb_meta_boxes', 9999 );
function wps_initialize_cmb_meta_boxes() {
if ( !class_exists( 'cmb_Meta_Box' ) ) {
require_once(CHILD_DIR . '/lib/metabox/init.php');
}
}
[/php]
Loren Nason says
Travis,
Your tutorials are awesome.
I had already had my own custom post type and was venturing into needing a meta box and then I found your post. I’ve followed everything adding all the required info for doing the meta_boxes and I’ve got nada.
So I thought there was something wrong with my file so I did everything exactly like you did it completely copying everything and still no metaboxes.
My file structure is exactly like you have
Even when setting up my files with your example code the CPT work just no metaboxes.
Also when making the wps-admin-functions.php I got an error on line 153
It needed a semi-colon
Still no metaboxes
Any pointers?
FYI it’s on a test domain and im monkeying around in agentpress child theme. (if that matters)
Loren Nason says
Got it working
$meta_boxes needed to be return $meta_boxes;
Travis Smith says
Thanks for pointing out my error. You’re right
$meta_boxes
should bereturn $meta_boxes;
My apologies! Don’t know how I missed that! Corrected this!Patrick Hess says
Hi Travis –
Thanks for this excellent tutorial – it’s been extremely helpful. I’ve been experimenting with the custom meta boxes for a few days now and I’m a little lost on the front-end implementation. I’ve read a few options for displaying the information, but none have really worked that well. Do you have a suggestion for displaying the meta per the example above?
I’m working from the Genesis sample child theme. Thanks for any info you can provide. I look forward to reading through the rest of your series (also, feel free to let me know if the front-end side is explained in later tutorials).
Appreciate it,
Patrick
Travis Smith says
Hello Patrick,
Hopefully, you read through the rest of the posts. Did they answer your question? Please let me know!
Thanks,
Leia says
Hi,
I have tried to copy your code and put it in lib folder but nothing happens, i dont get any new custom post types, any ideas what i have missed?
Travis Smith says
Hello Leia,
Did you add any code to functions.php to call the file?
Shaun says
is there a line to add in the functions.php in order for the wps-admin-functions.php to work?
Travis Smith says
Yes, you would need to use an include, include_once, require or require_once statement for it to work as a separate file. See PHP.net for more information.
Brooke says
Can you elaborate on this pretty please? I’ve gone to PHP.net searched for “include” saw some code, but don’t know how to incorporate it into my Genesis child them functions.php. Do I add an action onto the genesis_init hook? I mean I have absolutely no idea what I’m doing here!
Brooke says
ahaha, I’m such a dumb A, I got it!
// Include custom post types separate php file
require_once(STYLESHEETPATH.’/lib/wps-admin-functions.php’);
JohnLionFlow says
I used;
require_once( CHILD_DIR . ‘/lib/wps-admin-functions.php’ );
Thanks for bringing that Brooke
And a big thankyou to you Travis!
David P. Smith says
Thank you Travis!
After wasting an entire day on various tutorials (most of which worked, but were not as complete as this), I found yours. I could not get it to work, at first. But then I noticed, at the bottom, there is a call to the child theme’s directory. I’m not using a child theme. I replaced “CHILD_DIR” with “get_template_directory()” and all is well!
Andy says
I’ve followed your example here and copied the code as is into the wps-admin-functions.php file but I keep getting the error:
Parse error: syntax error, unexpected ‘}’ in THEME/nitrous/lib/wps-admin-functions.php on line 154
Any Ideas?
Travis Smith says
Yes, I corrected the master code at the end. Try again. Typo!
Jeff La Croix says
On line 153 shouldn’t it be “return $meta_boxes;”?
Travis Smith says
Yes, thanks!
carrie says
Hi Travis,
Thanks for the tutorial. For some reason, adding the include code for wps-admin-functions.php in my functions.php file is killing the site. I’ve tried variations on include, include_once, require_once, etc., but none are doing the trick.
include_once(CHILD_DIR.’/lib/wps-admin-functions.php’);
I was wondering if it was a permissions issue on wps-admin-functions.php not being allowed to execute, but I opened up permissions on it and that wasn’t a solution. Any trouble-shooting tips?
Thanks,
Carrie
Travis Smith says
Hello Carrie,
Did you remove the CMB files as you see here?
Thanks,
Travis
carrie says
Hi Travis,
If I use the <? php at the start, I just get a blank screen and have to fix the file via FTP. If I remove it, it outputs all the code of wps-admin-functions.php as text right after the opening tag. (See my dev site for this latter scenario in action: http://infusioncenter.net/dev/).
My directory structure is set up with lib/metabox/init.php (and the other github-files) and lib/wps-admin-functions.php. The LIB folder is located in my child theme.
Thanks,
Carrie
Nathan says
Was this ever hashed out? This would be perfect for something I’m working on, but I can’t get anything to show up, either. I have the same issue it looks like as Carrie is/was experiencing. I know this post is over a year old… is this methodology still relevant to Genesis 2.0 and WP 3.6?
Nathan says
I know this post is over a year old, but this is kind of a last-ditch effort:
I used this quite successfully as a template for setting up my own custom post types and everything is working beautifully except for just one thing:
In the posting panel in the WP admin/back-end, where I would assign the taxonomy, it’s more like assigning tags. The editor has my taxonomy, but instead of a list of checkboxes w/ custom taxonomies I have set, it wants me to enter tags.
For instance, my custom post type is ‘equipment’, and the taxonomy is ‘equipment family’. I have about 7 different categories under ‘equipment families’ but when I go to add a new piece of equipment, the ‘Equipment Families’ taxonomy panel gives me a text box w/ an ‘add’ button much like adding tags on a regular post; I’d like to find out how I can get it to give me a list of my configured taxonomy ‘families’ that I can put a checkbox by to assign the taxonomy.
Any input would be greatly appreciated. Thanks!
Newton Rodrigues says
Great post!
I was able to register my custom post type with your instructions. Now, what if want to register 10 custom taxonomies for this particular custom post type: will i should repeat that code 10 time each one of them for each particular taxonomy or there is an easier why to do in one go?
Thank you!
Travis Smith says
Newton,
Simply create an array and cycle through the array registering the taxonomies. Please let me know if you need more.
Thanks,
Travis