How to Create Custom Post Types and Taxonomies in WordPress
In this step-by-step tutorial, I’ll show you how to create custom post types and taxonomies in WordPress. What are the custom post types? Custom post types are posts with a different post_type
value in the database. WordPress has five post types ready for you: post
, page
, attachment
, revision
, and nav_menu_item
. You can create custom post types for books, events, movies, products, reviews, and so on. In this tutorial I’ll show you how to create a custom post type that will hold the items in a portfolio. What are custom taxonomies? Despite the name, custom taxonomies are just like categories and tags, a way to organize posts. Now, let’s get started, shall we?
Things that you must do:
- Install WordPress
- Open the
functions.php
file located in the directory of your WordPress theme.- Find the
load_theme_textdomain()
function. The first parameter of that function is yourtext_domain
.
- Find the
- In the code below, don’t forget to replace:
prefix_
with your prefix. The name of my WordPress theme istutsmix
. I could usetm_
as prefix. We use a prefix to avoid conflicts because we can’t have, for example, two functions with the same name.text_domain
with yourtext_domain
.
1. How to Create Custom Post Types
Please note that the code below goes into the functions.php
file.
/**
* Registers a custom post type.
*
* @link http://codex.wordpress.org/Function_Reference/register_post_type
*/
function prefix_register_post_type()
{
register_post_type(
'prefix_portfolio',
array(
'labels' => array(
'name' => __('Portfolio', 'text_domain'),
'singular_name' => __('Portfolio', 'text_domain'),
'menu_name' => __('Portfolio', 'text_domain'),
'name_admin_bar' => __('Portfolio Item', 'text_domain'),
'all_items' => __('All Items', 'text_domain'),
'add_new' => _x('Add New', 'prefix_portfolio', 'text_domain'),
'add_new_item' => __('Add New Item', 'text_domain'),
'edit_item' => __('Edit Item', 'text_domain'),
'new_item' => __('New Item', 'text_domain'),
'view_item' => __('View Item', 'text_domain'),
'search_items' => __('Search Items', 'text_domain'),
'not_found' => __('No items found.', 'text_domain'),
'not_found_in_trash' => __('No items found in Trash.', 'text_domain'),
'parent_item_colon' => __('Parent Items:', 'text_domain'),
),
'public' => true,
'menu_position' => 5,
'supports' => array(
'title',
'editor',
'thumbnail',
'excerpt',
'custom-fields',
),
'taxonomies' => array(
'prefix_portfolio_categories',
),
'has_archive' => true,
'rewrite' => array(
'slug' => 'portfolio',
),
)
);
}
add_action('init', 'prefix_register_post_type');
Above we’ve created the prefix_register_post_type()
function, and inside it we’ve registered a custom post type using the register_post_type()
function which accepts two parameters. The first parameter is the name of the custom post type which shouldn’t be longer than 20 characters, the second is the array of arguments. In the array of arguments we’ve defined a set of labels and the behavior of the custom post type. After that, we’ve attached the prefix_register_post_type()
function to the init
action hook. Now you should have a new entry called “Portfolio” on the left menu of your Dashboard. That’s awesome, right? Please note that a refresh may be needed. Let’s move on to the next step.
2. How to Display Custom Update Messages
Please note that the code below goes into the functions.php
file.
/**
* Sets the custom update messages for a custom post type.
*
* @param array $messages
* @return array
* @link http://codex.wordpress.org/Function_Reference/register_post_type
*/
function prefix_post_updated_messages($messages)
{
$post = get_post();
$post_type = get_post_type($post);
$post_type_object = get_post_type_object($post_type);
$messages['prefix_portfolio'] = array(
0 => '',
1 => __('Item updated.', 'text_domain'),
2 => __('Custom field updated.', 'text_domain'),
3 => __('Custom field deleted.', 'text_domain'),
4 => __('Item updated.', 'text_domain'),
5 => isset($_GET['revision']) ? sprintf(__('Item restored to revision from %s', 'text_domain'), wp_post_revision_title( (int) $_GET['revision'], false)) : false,
6 => __('Item published.', 'text_domain'),
7 => __('Item saved.', 'text_domain'),
8 => __('Item submitted.', 'text_domain'),
9 => sprintf(__('Item scheduled for: <strong>%1$s</strong>.', 'text_domain'), date_i18n(__('M j, Y @ G:i', 'text_domain'), strtotime($post->post_date))),
10 => __('Item draft updated.', 'text_domain'),
);
if ($post_type_object->publicly_queryable)
{
$permalink = get_permalink($post->ID);
$preview_permalink = add_query_arg('preview', 'true', $permalink);
switch ($post_type)
{
case 'prefix_portfolio':
$view_link = sprintf(' <a href="%s">%s</a>', esc_url($permalink), __('View item', 'text_domain'));
$preview_link = sprintf(' <a href="%s" target="_blank">%s</a>', esc_url($preview_permalink), __('Preview item', 'text_domain'));
break;
}
if (isset($view_link))
{
$messages[$post_type][1] .= $view_link;
$messages[$post_type][6] .= $view_link;
$messages[$post_type][9] .= $view_link;
}
if (isset($preview_link))
{
$messages[$post_type][8] .= $preview_link;
$messages[$post_type][10] .= $preview_link;
}
}
return $messages;
}
add_filter('post_updated_messages', 'prefix_post_updated_messages');
What are custom update messages? Custom update messages are the messages that WordPress displays when you publish a post, update one, and so on. Those messages are tailored to regular posts. The posts in our custom post type are called “items”. We must define custom update messages because we don’t want to see, for example, “Post published.” instead of “Item published.”.
Above, we’ve created the prefix_post_updated_messages()
function, and inside it we’ve defined the custom update messages. After that, we’ve attached the prefix_post_updated_messages()
function to the post_updated_messages
filter hook. Now, for example, if you publish a post, you should see a custom update message. Are you ready to move on to the next step? Let’s do this then.
3. How to Create Custom Taxonomies
Please note that the code below goes into the functions.php
file.
/**
* Registers a custom taxonomy.
*
* @link http://codex.wordpress.org/Function_Reference/register_taxonomy
*/
function prefix_register_taxonomy()
{
register_taxonomy(
'prefix_portfolio_categories',
array(
'prefix_portfolio',
),
array(
'labels' => array(
'name' => _x('Categories', 'prefix_portfolio', 'text_domain'),
'singular_name' => _x('Category', 'prefix_portfolio', 'text_domain'),
'menu_name' => __('Categories', 'text_domain'),
'all_items' => __('All Categories', 'text_domain'),
'edit_item' => __('Edit Category', 'text_domain'),
'view_item' => __('View Category', 'text_domain'),
'update_item' => __('Update Category', 'text_domain'),
'add_new_item' => __('Add New Category', 'text_domain'),
'new_item_name' => __('New Category Name', 'text_domain'),
'parent_item' => __('Parent Category', 'text_domain'),
'parent_item_colon' => __('Parent Category:', 'text_domain'),
'search_items' => __('Search Categories', 'text_domain'),
),
'show_admin_column' => true,
'hierarchical' => true,
'rewrite' => array(
'slug' => 'portfolio/category',
),
)
);
}
add_action('init', 'prefix_register_taxonomy', 0);
Above, we’ve created the prefix_register_taxonomy ()
function, and inside it we’ve registered a custom taxonomy using the register_taxonomy ()
function which accepts three parameters. The first parameter is the name of the custom taxonomy, which shouldn’t be longer than 32 characters. The second is the name of the custom post type, and the third is the array of arguments. In the array of arguments, we’ve defined a set of labels and the behavior of the custom taxonomy. After that, we’ve attached the prefix_register_taxonomy ()
function to the init
action hook. Now you should have a new entry called “Categories” in the “Portfolio” section of your Dashboard. Please note that a refresh may be needed. Are you still with me? Alright, let’s move on then.
4. How to Flush the Rewrite Rules
Please note that the code below goes into the functions.php
file.
/**
* Flushes the rewrite rules.
*
* @link http://codex.wordpress.org/Function_Reference/register_post_type
*/
function prefix_flush_rewrite_rules()
{
flush_rewrite_rules();
}
add_action('after_switch_theme', 'prefix_flush_rewrite_rules');
Custom post types get their own URL (e.g. http://example.com/post_type/
). http://example.com/prefix_portfolio/
in our case, but because we’ve defined a custom slug, that URL becomes http://example.com/portfolio/
.
Custom taxonomies get their own URL too (e.g. http://example.com/taxonomy/
). http://example.com/prefix_portfolio_categories/
in our case, but because we’ve defined a custom slug, that URL becomes http://example.com/portfolio/category/
.
For custom slugs to work, we must flush the rewrite rules.
Above we’ve created the prefix_flush_rewrite_rules()
function, and inside it we’ve called the flush_rewrite_rules()
function, which will flush the rewrite rules. After that, we’ve attached the prefix_flush_rewrite_rules()
function to the after_switch_theme
action hook. Now all you have to do is to reactivate your WordPress theme or to visit the “Settings → Permalinks” section of your Dashboard. After that, everything should work smoothly. We’re almost done. Hang in there!
5. How to Display the Posts of a Custom Post Type
To display the posts of a custom post type, WordPress will use the first file that it finds in this order:
archive-post_type.php
(e.g.archive-prefix_portfolio.php
)archive.php
index.php
To display single posts, WordPress will use the first file that it finds in this order:
single-post_type.php
(e.g.single-prefix_portfolio.php
)single.php
index.php
You can use the code below to display the posts of a custom post type using the archive-post_type.php
file.
<?php if (have_posts()): ?>
<?php while (have_posts()): ?>
<?php the_post(); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
<?php else: ?>
<h1 class="page-title">No Posts Found</h1>
<?php endif; ?>
You can use the code below to display single posts using the single-post_type.php
file.
<?php while (have_posts()): ?>
<?php the_post(); ?>
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
You can use the code below to display the posts of a custom post type using a custom query with the WP_Query
class.
<?php
$prefix_posts = new WP_Query(
array(
'post_type' => 'prefix_portfolio',
)
);
?>
<?php if ($prefix_posts->have_posts()): ?>
<?php while ($prefix_posts->have_posts()): ?>
<?php $prefix_posts->the_post(); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
<?php else: ?>
<h1 class="page-title">No Posts Found</h1>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
You can use the code below to display the posts of a custom post type from certain taxonomies using a custom query with the WP_Query
class.
<?php
$prefix_posts = new WP_Query(
array(
'post_type' => 'prefix_portfolio',
'tax_query' => array(
array(
'taxonomy' => 'prefix_portfolio_categories',
'field' => 'slug',
'terms' => array(
'branding',
),
),
),
)
);
?>
<?php if ($prefix_posts->have_posts()): ?>
<?php while ($prefix_posts->have_posts()): ?>
<?php $prefix_posts->the_post(); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
<?php else: ?>
<h1 class="page-title">No Posts Found</h1>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
6. How to Display the Posts of a Custom Taxonomy
To display the posts of a custom taxonomy, WordPress will use the first file that it finds in this order:
taxonomy-taxonomy-term.php
(e.g.taxonomy-prefix_portfolio_categories-branding.php
)taxonomy-taxonomy.php
(e.g.taxonomy-prefix_portfolio_categories.php
)taxonomy.php
archive.php
index.php
You can use the code below to display the posts of a custom taxonomy using the taxonomy-taxonomy.php
file.
<?php if (have_posts()): ?>
<?php while (have_posts()): ?>
<?php the_post(); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
<?php else: ?>
<h1 class="page-title">No Posts Found</h1>
<?php endif; ?>
You can use the code below to display the posts of a custom taxonomy using a custom query with the WP_Query
class.
<?php
$prefix_posts = new WP_Query(
array(
'tax_query' => array(
array(
'taxonomy' => 'prefix_portfolio_categories',
'field' => 'slug',
'terms' => array(
'branding',
),
),
),
)
);
?>
<?php if ($prefix_posts->have_posts()): ?>
<?php while ($prefix_posts->have_posts()): ?>
<?php $prefix_posts->the_post(); ?>
<h2 class="entry-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
<div class="entry-content"><?php the_content(); ?></div>
<?php endwhile; ?>
<?php else: ?>
<h1 class="page-title">No Posts Found</h1>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
Final Thoughts
We’re done. You’ve learned how to create custom post types and taxonomies in WordPress. You’ve also learned how to display their posts using templates and custom queries. I’m very proud of you. I’ve tried to guide you as best I could, and I hope that this step-by-step tutorial was helpful to you.