WordPress 3 comes with the ability to create Custom Post Types, which makes it very simple for us developers to extend WordPress and use it as a proper CMS for any business need. Apart from the default Posts and Pages, we can now create custom types to better suit our content, and make it easier for users to update the site.
First up, to clear some misconceptions, Custom Post Types should be thought of more as Custom Content Types, as this avoids the confusion with WordPress inbuilt Posts. So with the advent of Custom Post Types we now have the following possibilities:
- Posts
WP default content type, normally used for blog posts. Posts are normally displayed in the blog in reverse sequential order by time (newest posts first). Posts are also used for creating the feeds. - Pages
WP default content type, normally used for static pages. They are similar to posts but live outside the normal time based structure of posts. They can also use special Page Templates to display them. Pages can also be organized in a hierarchical structure, with Pages being parents to other Pages. - Custom Post Types
Can be anything we want them to be as we create and customise them ourselves. Think of them as Custom Content Types.
To be 100% accurate I must also mention that Attachments, Revisions and Nav Menus are also default WordPress post types.
Internally, post types are all stored in the same place, in the wp_posts table. These are differentiated by a field called post_type.
Custom Post Types can be created manually or through a plugin. I tend to prefer the manual way as it gives you the most flexibility and is the only way to properly learn how custom post types are constructed. If you have no experience in coding however, there are some plugins which make it much easier for you to create custom post types.
Let’s take a look at how to create Custom Post Types using a plugin first, then do the same in a manual way.
Creating Custom Post Types, Metaboxes and Custom Fields via Plugins
I did mention above that I prefer the manual way to creating custom post types. However on many a project recently I’ve been leaning on a new gem of a plugin. Actually, it’s not one plugin but two plugins that go hand in hand and are by far the most powerful plugins for custom post types and fields. I’m talking about Types and Views.
Introduction to Types
Types includes everything a web developer needs to customize the WordPress admin. The base Types plugin includes:
- Custom post types management
- Custom fields management
- Post relationship management
You can get additional functionality using Types add-on plugin:
- Types Access – comprehensive roles access management for different content types
[youtube]http://www.youtube.com/watch?v=unUbOoRaQmE[/youtube]
Introduction to Views
Views is an amazing plugin that aids you in displaying custom fields in your theme. It not only helps you display the fields but also do more advanced operations with them, and is unique in its area.
[youtube]http://www.youtube.com/watch?v=WQOlHu9nVkQ[/youtube]
Definitely check this hot pair of plugins out!
Download Views | Download Types
Another great plugin I’ve found for custom fields is the aptly titled Advanced Custom Fields. You can also buy add-ons through the developer’s site, but the main plugin is free. Definitely one to check out if you’re looking for a way to really do some advanced customisations through custom fields, such as adding layout fields, repeating fields, etc.
If you’re looking for a plugin with which you can not only create custom post types, custom fields, and custom taxonomies, but also export them for use in themes (without a plugin), then your answer is Easy Custom Content Types by Pippin Williamson.
Creating a Custom Post Type Manually
In order to learn how to create custom post types manually, we can use another example. This time I will be creating a somewhat simpler custom type called ‘Websites’. This post will be used for displaying interesting websites which I recommend. It could easily be used as a portfolio if you are a web designer, for example.
In our example, I want to have the following fields in my Websites content type:
- Title
- Description
- URL
- Screenshot
I will be using the WP default title and editor for the first two fields, then add a custom meta box for the other two.
First up, I need to define the new custom post type in functions.php:
[php]
/* Add Websites Custom Post Type */
add_action( ‘init’, ‘create_website_type’ );
function create_website_type() {
register_post_type( ‘website’,
array(
‘labels’ => array(
‘name’ => __( ‘Websites’ ),
‘singular_name’ => __( ‘Website’ ),
‘add_new’ => __( ‘Add New Website’ ),
‘add_new_item’ => __( ‘Add New Website’ ),
‘edit’ => __( ‘Edit Website’ ),
‘edit_item’ => __( ‘Edit Website’ ),
‘new_item’ => __( ‘Add New Website’ ),
‘view’ => __( ‘View Website’ ),
‘view_item’ => __( ‘View Website’ ),
‘search_items’ => __( ‘Search Websites’ ),
‘not_found’ => __( ‘No Websites Found’ ),
‘not_found_in_trash’ => __( ‘No Websites found in Trash’ ),
),
‘description’ => __(‘Websites to be shown in Resources section.’),
‘public’ => true,
‘show_ui’ => true,
‘publicly_queryable’ => true,
‘exclude_from_search’ => false,
‘menu_position’ => 20,
‘supports’ => array( ‘nothing’ ),
‘can_export’ => true
)
);
}
[/php]
I won’t be going through the parameters but you could read about them in the WordPress Codex Function Reference: register_post_type.
Note that the above code creates a custom post type with page-like functionality. If you would like the custom post type to behave more like a post, with no hierarchical options but the ability to associate to tags and categories, you can add the following parameters just below the ‘can_export’ parameter:
[php]
‘hierarchichal’ => false,
‘capability_type’ => ‘post’,
[/php]
Creating Custom Meta Boxes
Once we have registered our new post type we should be able to see it in the WordPress admin. It is now time to add the custom meta box. I am going to call it ‘Website Details’ and it will contain the fields ‘url’ and ‘screenshot’:
[php]
/* Add Custom Meta Box to Websites Post Type */
$new_meta_boxes =
array(
‘url’ => array(
‘name’ => ‘url’,
‘std’ => ”,
‘title’ => ‘URL’,
‘description’ => ‘Enter the site’s URL here.’),
‘screenshot’ => array(
‘name’ => ‘screenshot’,
‘std’ => ”,
‘title’ => ‘Screenshot’,
‘description’ => ‘Enter a link to the screenshot of the site.’)
);
function new_meta_boxes() {
global $post, $new_meta_boxes;
foreach ($new_meta_boxes as $meta_box) {
$meta_box_value = get_post_meta($post->ID, $meta_box[‘name’], true);
if($meta_box_value == ”)
$meta_box_value = $meta_box[‘std’];
echo’<input id=”.$meta_box[‘name’].’_noncename’ type=’hidden’ name=”.$meta_box[‘name’].’_noncename’ value=”.wp_create_nonce( plugin_basename(__FILE__) ).” />’;
echo’
<strong>’.$meta_box[‘title’].’</strong>’;
echo’<input style=’width: 100%; margin-top: 5px;’ type=’text’ name=”.$meta_box[‘name’].” value=”.$meta_box_value.” />
‘;
echo’
<em><label for=”.$meta_box[‘name’].”>’.$meta_box[‘description’].’</label></em>
‘;
}
}
function create_meta_box() {
if ( function_exists(‘add_meta_box’) ) {
add_meta_box( ‘new-meta-boxes’, ‘Website Details’, ‘new_meta_boxes’, ‘website’, ‘normal’, ‘high’ );
}
}
function save_postdata( $post_id ) {
global $post, $new_meta_boxes;
foreach($new_meta_boxes as $meta_box) {
// Verify
if ( !wp_verify_nonce( $_POST[$meta_box[‘name’].’_noncename’], plugin_basename(__FILE__) )) {
return $post_id;
}
if ( ‘page’ == $_POST[‘post_type’] ) {
if ( !current_user_can( ‘edit_page’, $post_id ))
return $post_id;
} else {
if ( !current_user_can( ‘edit_post’, $post_id ))
return $post_id;
}
$data = $_POST[$meta_box[‘name’]];
if(get_post_meta($post_id, $meta_box[‘name’]) == ”)
add_post_meta($post_id, $meta_box[‘name’], $data, true);
elseif($data != get_post_meta($post_id, $meta_box[‘name’], true))
update_post_meta($post_id, $meta_box[‘name’], $data);
elseif($data == ”)
delete_post_meta($post_id, $meta_box[‘name’], get_post_meta($post_id, $meta_box[‘name’], true));
}
}
add_action(‘admin_menu’, ‘create_meta_box’);
add_action(‘save_post’, ‘save_postdata’);
[/php]
You might have noticed in the code above, that we are using Nonces for security purposes. If you don’t know what that is, I suggest you check out this excellent post about WP nonces on Mark Jaquith’s blog.
A good place to learn about adding new meta boxes is the WordPress Codex, specifically the function reference for add_meta_box.
Another excellent tutorial for creating meta boxes, also including file and image upload can be found in this article on DeluxeBlogTips.
An excellent class that simplifies the creation of metaboxes is WPAlchemy Metabox.
Another plugin which facilitates creation of metaboxes, without a UI (aimed towards developers: Custom-Metadata
Choosing Columns to Display in Post List
Next you can also edit the columns displayed in your list view:
[php]
/* Customise columns shown in list of custom post type */
add_action(‘manage_posts_custom_column’, ‘my_custom_columns’);
add_filter(‘manage_edit-website_columns’, ‘my_website_columns’);
function my_website_columns($columns)
{
$columns = array(
‘cb’ => ‘<input type=’"checkbox"’ />’,
‘title’ => ‘Website’,
‘url’ => ‘URL’,
);
return $columns;
}
function my_custom_columns($column)
{
global $post;
if (‘ID’ == $column) echo $post->ID;
elseif (‘url’ == $column) {
$url = get_post_meta($post->ID, ‘url’, $single=true);
echo ‘<a href=’$url’>$url</a>’;
}
}
[/php]
Note: If you delete the code for creating custom post types from your functions.php, or uninstall a custom post types plugin, the custom posts you had created will still remain in the database.
Displaying Custom Post Types
To display custom post types in your front end, you could make a loop as follows:
[php]
<?php $loop = new WP_Query( array( ‘post_type’ => ‘website’, ‘posts_per_page’ => 0 ) );
while ( $loop->have_posts() ) : $loop->the_post(); ?>
<?php the_title( ‘<br ?>
<h2><a title=” . the_title_attribute( ‘echo=0′ ) . ” href=” . get_permalink() . ” rel=’bookmark’>’, ‘</a></h2>
‘ ); ?>
<div><?php the_content(); ?>
<?php if(get_post_meta($post->ID, ‘screenshot’, $single = true) != ”) :
?>
<img src=’&amp;amp;quot;<?php’ alt=” />ID, ‘screenshot’, $single = true); ?>’ alt=’<?php the_title(); ?>’ />
<?php endif; if(get_post_meta($post->ID, ‘url’, $single = true) != ”) :
echo get_post_meta($post->ID, ‘url’, $single = true);
endif;
?></div>
<?php endwhile; ?>
[/php]
Listing Pages of a Custom Post Type
[php]<?php wp_list_pages( array( ‘post_type’, ‘something’ ) ); ?>[/php]
Themes, Plugins and Websites Which Implement Custom Post Types
A number of themes and plugins implement the Custom Post Types functionality and are an excellent source of reference when learning about this functionality. Here are a few of them:
- Quality Control
A free innovative theme using custom post types to manage bug reports. - The Events Calendar
Uses custom post types to create events. Allows you to set a start and end time, location and address. - Estate
A real estate theme by Woo Themes that uses custom post types for property listings. - WP Bids
Uses a custom post type to generate client proposals and job estimates. - TBTestimonials
Manage your client testimonials with Custom Post Types. There’s even a spiffy widget. - Rotary
A free theme with a slider for custom post type images. - CPT Speakers
This plugin creates a custom post type for event speakers. Includes speakers page and single-speakers page templates.
WPMayor also has two sections powered by Custom Post Types. The ‘Books’ section was created using the More Types plugin while the ‘Websites’ section was created manually using code in the theme’s functions.php file.
Other Custom Post Type Plugins
Post Type Switcher
What happens when you already have posts and want to switch them over to your new custom post type? Or just want to change a Post into a Page, for example? Enter Post Type Switcher, a really cool plugin that makes you do that with no fuss. After installing it, the plugin will add a ‘Post Type’ interface on your ‘Edit Post’ screen, just above the ‘Publish’ button. You can use this interface to change post types as required.
Changing Custom Post Type Order
If you would like to change the order of custom post types the Custom Post Type Order plugin lets you do just that, and features a drag and drop interface that is really easy and ituitive to use.
Posts to Posts for Many to Many Relationships
The Posts to Posts plugin allows you to create many-to-many relationships between posts of all types. The relationships are stored in a hidden taxonomy. Check out the documentation explaining its usage.
Manually Selecting Related Posts
The Custom Post Type Relationships plugin allows you to set relationships between your posts (as an alternative to automated tools such as YARPP). Most of the “related posts” plugins out there while they provide some kind of control on how to create related posts, they all rely on an algorithm and the results are automatic. Custom Posts Types Relationships is a WordPress plugin that allows you to define relationships between posts in a manual way. This second version of the plugin now supports posts, pages and custom posts types, as well as allow you to define cross-type relationships. Sure, plugins that automatically get the job done are nice, but cat take you that far as they might link to irrelevant posts or change the output as more posts are added.
Custom Post Type Archives
The Custom Post Type Archives plugin will enable custom post type archives (also yearly, monthly and daily) together with feeds, customizable titles and paging. These archives will work the same way as the category or tag archives work when you go to, for example, http://example.com/category/example.
Custom Post Type Permalinks
The Custom Post Permalinks plugin will set up custom post type permalinks for non-hierarchical permalinks which have the flexibility of blog post permalinks. So, for example, I could have a press release post type and set up permalinks like so:
- http://my-site.com/press-releases/2010/08/great-news/
which would also enable post type specific archives:
- http://my-site.com/press-releases/2010/08/
- http://my-site.com/press-releases/2010/
Alternatives to Custom Post Types
If for some reason you are not happy with Custom Post Types I recommend checking out Pods, which delivers the same basic functionality as Custom Post Types, but is an altogether different beast with some important differences in the way it implements custom content types. Going into it is out of the scope of this post, but do check out the website of this ambitious CMS framework project especially if you are doing some advanced content management and need a very flexible system.
34 Responses
Very nice write-up. I abslutely appreciate this site.
Continue the good work!
Great post! thanx 🙂
Welcome 🙂
Do you like Types more than Easy Content Types [i.e., independent of views]? Is it possible to use Easy Content Types in conjunction with views?
Also, one month later – what are you thoughts about Views?
Yeah Types and Views are definitely leaders now when it comes to custom post types, custom fields and metaboxes. This is the only plugin combination I use nowadays.
Hello! Thanks for sharing your vast information with the rest of us.
I have a question, and I know it must have a somewhat simple answer, but I guess I don’t know how to word it correctly to get a decent answer in a Google search.
I have manually inserted a Custom Post Type on my front page that works fine.
Actually I am using Excerpts on the front page in a side column.
I want the “Read more” link to link to a custom page template for my Custom Post Type entries only instead of the generic page template.
How do I accomplish this? Is there a way to associate a Custom Template in the function that creates the Custom Post Type in functions.php?
Any help you can offer would be greatly appreciated.
Thanks!
I did find an answer to my own question on https://codex.wordpress.org/Post_Types . I had apparently overlooked it several times.
“In the same way that posts are shown on their own page with single.php, custom post types will use single-{posttype}.php if it’s available.”
I renamed my custom page using that manner and it works beautifully.
Glad you managed in the end!
Thanks Jean.
Perhaps you can help out with an issue I’m having? I’m using a shortcode to insert custom post content into a regular post content. Unfortunately, the share plugin I’ve been using (Socialable) adds share links to both the reg post *and* the inserted custom post.
Is there a way to force add_filter(‘the_content,…) to only filter standard posts and ignore custom posts?
If you have any suggestions and can ping me via email that would be great. Thanks.
Very very helpful, thank you. 😉
Wow, excellent article 😉
Jean a question:
I need a search engine in WordPress, which is able to see beyond simple pages and post, also post type, taxonomies and custom field.
Some time ago I used “wp custom search field”, but with the new version of wordpress does not work anymore.
You have found some solution to this problem?
Thank you very much 😉
Thanks for the tutorial
created the books [posttype] and additional fields but I can’t make the books apear on the website. It won’t show. Do you have an additional tutorial for creating a suitable working template for it?
John
Hi thanks for the tutorial I’m getting an error:
Notice: Undefined index: url_noncename in /Users/admin/Dropbox/Sites/pianocourse/wp-content/themes/pianocourse/custom-posts.php on line 380
any ideas? Many thanks
this is wonderful man. thanks for the compilation!
Thanks John.
Hey Jean,
Great post! One of the more useful I’ve found on the subject.
I do have a question: I’m using a custom post type, with the Custom Post Type Relationship plugin. Using their ‘get’ function, with ‘get_post_meta’, I’m pulling custom post types that are related to the single post page that I’m currently on.
My problem is that I’m trying to get the title of the custom post to show, but the custom post type I created is using the normal post title field. How do I pull that single piece of data onto my page?
If you have any thoughts, I’d love to hear them. Thanks,
Dave
Hi Dave, glad you found it useful. I haven’t played around that much with that plugin, you might get a quicker answer on the plugin’s forum or on wordpress.stackexchange.com. Post back if you don’t get a reply from there and I’ll try to investigate further.
You can also try out the Posts 2 Posts plugin which I believe is better than the one you are currently using.
No not really.
Let’s say I have a relational database with books – in one table I have all the books, in another table all the authors biographies, in another table I have information about publisher. The whole database is relational, so that each book relate to the tables with author biographies and information about the publisher. Now on my wordpress website I want one book pr. page, and I want so that the author biographies and publisher information is visible for each book. When I update the author biography, or the publisher information then it is updated through out the site on every page about every book.
So what I would like it this, I want to be able to go and make a new book post, and then select in a drop down list, who is the author and who is the publisher. That should then display the author biography, and some short information about the publisher on the page about the book.
But I also need to be able to update the information, that is go in and ad information about the author biographies in one place, and have it effect every page with books by that author.
Does that make sense?
Thanks a lot for taking time to reading this, and hopefully understand 🙂
Ok I understood now, you need to take a look at this plugin:
Posts 2 Posts and the documentation where its usage is explained very well.
Also check out Pods which is a bit more advanced.
Let us know how it goes.
Hi Jean
Thanks for this great tutorial. In your example – with books, it would make sense if you could get a dropdown menu for authors, publishers etc. Like in a relational database, you should then only have to update in one place if you need to update a lot of pages with a book on each page. Hope you see what I mean – is this possible?
Thanks in advance for any help
Hi Mads, thanks for your comment. Let’s see if I’m understanding this well. Are you speaking about having the option to tick a few books from the books list, and use a dropdown to set a new author?
some really really great stuff over here…and a great list of plugins too..thank you for sharing
Glad to be of help!
Excellent! You’ve definitely given one of the most well-rounded resources out there. And thanks for your comments about Easy Content Types!
Thanks and welcome Pippin.
I’m a beginner with WP, but I guess I’ll go around coding it some day too.
It is the great article which covers everything about the custom content type in wordpress. Great effort has been made to list each and every option to create custom content type. Thanks for helping the wordpress community.
Welcome Arun, thanks for taking the time to leave a comment.
CHEERS! Great work… a really useful article I’ll be bookmarking as a reference… thanks so much! =)
I’m thinking of using Custom Post Types on a current project and this is by far the most complete resource that I’ve found so far. Thanks very much!
Do you have any examples of WordPress sites that make use of Custom Post Types that you’d be willing to share? I think this would help myself and others get a better sense of what’s possible.
Thanks Tim, many sites are now using Custom Post Types, although it is not always obvious from the outside that they are using them. You can take a look at the themes and plugins listed above, or else at this site’s own Resources section which is built using Custom Post Types. Another site which I built using CPT is Honda.com.mt.
This is one of the best WordPress tutorials out there. Custom Post Types is essential for transforming WordPress itself from a blogging platform to a complete Content Management System.
Well done!
this is an excellent resource. good work.