A couple of months ago, I did a comprehensive overview on how to set up custom post types, taxonomies, and custom meta boxes in your WordPress installation. Now I will revisit custom post types and taxonomies, except this time we will use efficiency rather than learning as our guiding principle and go over the quickest way to set up customization of post types for WordPress.

Download File

cpt-setup.zip (6.76 kb) Download cpt-setup.zip

This time we will be relying on other plugins to generate custom meta boxes, so all we need to worry about is setting up the taxonomies and custom posts. You may follow along with the tutorial to write your own code, or download the above plugin and edit it as appropriate.

Plugin or functions.php? The advantage to using a plugin for Custom Post Types is that plugins will persist if you elect to change your theme. functions.php files are tied to their themes. This isn’t a fool-proof catch-all, as Custom Post Types and taxonomies require certain theme files in order to display, but it will eliminate the need to remember to copy over a significant portion of your theme’s functions if you use a plugin. Using a plugin also allows you to deactivate the CPTs if desired. Ultimately though, the choice is yours.

If you’re writing a plugin to handle your custom posts rather than putting them in your theme’s functions.php file, you need to begin your PHP file with some general plugin information. (Skip this step if you’re using functions.php.)

<?php
/**
 * @package Setup_Custom_Post
 * @version 1.0
 */
/*
Plugin Name: Setup Custom Post Type
Plugin URI: http://blog.kim-maida.com/web-development/wordpress/custom-post-types-revisited
Description: A sample "template" custom post type plugin for editing. 
			
Author: Kim Maida
Version: 1.0
Author URI: http://kim-maida.com
License: Free
*/

We’ll start by setting up the filter to enable our custom post type to share categories and post tags with standard posts. Leave this off if you do not want this functionality.

// Filter to enable custom post types to share categories and tags with regular posts
//--- Remove this section if your custom post will not be sharing any categories or tags
//--- May require further editing if you will be adding additional custom post types

add_filter('pre_get_posts', 'query_post_type');
function query_post_type($query) {
	if(is_category() || is_tag()) {
		$post_type = get_query_var('post_type');
	if($post_type)
		$post_type = $post_type;
	else
		$post_type = array(
			'post',
			'custom_post'	 // add custom post type(s) here
		);
	$query->set('post_type',$post_type);
	return $query;
	}
}

Set up the custom taxonomies next. The reason we are doing this before initializing the new custom post type is because we want to rewrite the taxonomy slugs to attach them to the custom post type slug. What this means is that taxonomy archives will be rewritten to the format of: URL/custom-post-type/taxonomy/term, instead of: URL/taxonomy/term.

You will want to leave this particular rewrite functionality off if you are sharing custom taxonomies among multiple post types.

Here is some code registering a hierarchical (category-like, with parent/child structure) taxonomy:

//-------------------------- CUSTOM TAXONOMIES
//-------------------------- http://codex.wordpress.org/Taxonomies#Custom_Taxonomies

add_action( 'init', 'create_hierarchical_taxonomy', 0 );
function create_hierarchical_taxonomy() 
{  
	// The labels that your custom taxonomy will use in the WordPress admin UI
    $labels = array(
    	'name' => _x( 'Hierarchical Taxonomy', 'taxonomy general name' ),
		'singular_label' => _x( 'Hierarchical Taxonomy', 'taxonomy singular name' ),
		'search_items' => __( 'Search Hierarchical Taxonomies' ),
		'all_items' => __( 'All Hierarchical Taxonomies' ),
		'parent_item' => __( 'Parent Hierarchical Taxonomy' ),
		'parent_item_colon' => __( 'Hierarchical Taxonomy' ),
		'edit_item' => __( 'Edit Hierarchical Taxonomy' ),
		'update_item' => __( 'Update Hierarchical Taxonomy' ),
		'add_new_item' => __( 'Add New Hierarchical Taxonomy' ),
		'new_item_name' => __( 'New Hierarchical Taxonomy Name' )
    );
	    
    // http://codex.wordpress.org/Function_Reference/register_taxonomy
	register_taxonomy(
		'h_taxonomy', // internal name
		'custom_post', // object type - what post type can use this taxonomy, can also be an array of post types: array( 'post', 'page')
		array(
			// http://codex.wordpress.org/Function_Reference/register_taxonomy#Arguments
			'labels' => $labels,	// Instructs use of the labels specified above
			'hierarchical' => true, // True: tree-structure like categories  
			'rewrite' => array( 'slug' => 'custom-post/hierarchical-taxonomy', 'with_front' => false),	// Applies a rewrite associating this taxonomy with the specified CPT
			'query_var' => true
		)
	);  
}

Here is sample code for registering a non-hierarchical (free-form, like tags) taxonomy:

add_action( 'init', 'create_nh_taxonomy', 0 );
function create_nh_taxonomy() 
{  
	// The labels that your custom taxonomy will use in the WordPress admin UI
    $labels = array(
    	'name' => _x( 'Non-hierarchical Taxonomy', 'taxonomy general name' ),
		'singular_label' => _x( 'Non-hierarchical Taxonomy', 'taxonomy singular name' ),
		'search_items' => __( 'Search Non-hierarchical Taxonomies' ),
		'all_items' => __( 'All Non-hierarchical Taxonomies' ),
		'parent_item' => __( 'Parent Non-hierarchical Taxonomy' ),
		'parent_item_colon' => __( 'Parent Non-hierarchical Taxonomy' ),
		'edit_item' => __( 'Edit Non-hierarchical Taxonomy' ),
		'update_item' => __( 'Update Non-hierarchical Taxonomy' ),
		'add_new_item' => __( 'Add New Non-hierarchical Taxonomy' ),
		'new_item_name' => __( 'New Non-hierarchical Taxonomy Name' ),
		'separate_items_with_commas' => __( 'Separate Non-hierarchical Taxonomies with commas' ),
		'choose_from_most_used' => __( 'Choose from most used Non-hierarchical Taxonomies' )
    );
	    
    // http://codex.wordpress.org/Function_Reference/register_taxonomy
	register_taxonomy(
		'nh_taxonomy', // internal name
		'custom_post', // object type - what post type can use this taxonomy, can also be an array of post types: array( 'post', 'page')
		array(
			// http://codex.wordpress.org/Function_Reference/register_taxonomy#Arguments
			'labels' => $labels,	// Instructs use of the labels specified above
			'hierarchical' => false, // False: free-form structure like tags  
			'rewrite' => array( 'slug' => 'custom-post/nonhierarchical-taxonomy', 'with_front' => false), // Applies a rewrite associating this taxonomy with the specified CPT
			'query_var' => true
		)
	);  
}

Next, register the custom post type. Note that the rewrite slug for the CPT should match the slug you specified to come before your associated custom taxonomies. (Ie: if the taxonomy rewrite was cpt-slug/taxonomy, the CPT rewrite slug needs to be cpt-slug.)

//-------------------------- CUSTOM POST TYPE
//-------------------------- http://codex.wordpress.org/Post_Types#Custom_Types

add_action('init', 'create_custom_post');        
function create_custom_post() 
{  
	// The labels your custom post type will use in the WordPress admin UI
	$labels = array(
		'name' => _x( 'Custom Post', 'custom post' ),
		'singular_name' => _x( 'Custom Post', 'custom post' ),
		'all_items' => __( 'All Custom Posts' ),
		'add_new' => __( 'Add New Custom Post' ),
		'add_new_item' => __( 'Add New Custom Post' ),
		'edit_item' => __( 'Edit Custom Post' ),
		'menu_name' => _x( 'Custom Posts', 'custom posts' ),
	); 
	
	// http://codex.wordpress.org/Function_Reference/register_post_type#Arguments
	$args = array(  
		'labels' => $labels,	// Instructs use of the labels specified above  
		'description' => _x( 'A default custom post type for customizing.', 'Short descriptive summary of the post type' ), 
		'public' => true, 	// Meta argument to define default values for publicly_queriable, show_ui, show_in_nav_menus, exclude_from_search   
		'menu_position' => 5,	// 5 = Below posts (see WP codex for more information on menu position)
		'hierarchical' => false,	// True: parent structure like pages | False: like posts
		'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'revisions', 'author', 'discussion', 'comments'),	// Alias for add_post_type_support()
		'taxonomies' => array('category','post_tag'), // Shares these with other posts: needs a query function for archives to work!
		'has_archive' => __('custom-posts'),	// Enables custom post type archives: boolean or string (string will be the archive slug)
		'rewrite' => array('slug' => 'custom-post', 'with_front' => false )	// Rewrite permalinks, create a slug for the custom post type
	);    
    
	// http://codex.wordpress.org/Function_Reference/register_post_type
	register_post_type( 
		'custom_post',	// internal name
		$args 	// arguments from the array above
	);  
}

We should flush rewrite on activation to prevent 404 errors. If you continue to have 404s on archive pages, etc., visit the Permalink settings and re-save. (Also noted below.)

// Flush rewrite on activation: this should prevent 404 errors when trying to view custom posts and archives
add_action('init', 'create_custom_post');
function my_rewrite_flush() {
	create_custom_post();
	flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'my_rewrite_flush');

Finally, if you’re putting this code in a plugin instead of your theme’s functions.php, end the plugin:

// end the plugin ?>

Now that we have taxonomies and custom post types set up, we can create custom fieldgroups to use. This can be done by hand by constructing custom meta boxes, as previously covered in a different post, but the fastest and easiest way to do this is to use a robust custom fields plugin. I recommend Advanced Custom Fields. This plugin allows you to associate fieldgroups with different post types. It supports a variety of field types, file uploads, etc. and also uses short tags to get and display the custom field data. Visit the Advanced Custom Fields developer’s website for full documentation.

See the custom post types archives for additional articles on how to set up and use custom post types in WordPress.

Getting 404 errors on custom taxonomy archives? Before you tear your hair out unable to figure out where you went wrong in the code, try visiting your admin section’s Settings / Permalinks and hit “Save Changes” to flush the cache. This should fix those confounding Not Founds. If they persist after doing this, then check the code.