For those of us who haven’t been living under a rock for the last 6 years, we know that since WordPress 3.0 release we are now free to create our own post types. Basically, you can set a standard ‘format’ to display a certain ‘type’ of content via custom post types.
This article will guide you step-by-step through that process. But first, let’s make note of a few things, just to brush up on our WordPress knowledge.
There are five post types that available by default on WordPress:
- Post (post_type: 'post') used for creating feeds, post is the type used by most blogs to display one published post at a time.
- Page (post_type: 'page') same as posts, with some differences. Page type use templates, are not bound by time based listings (as is the case of typical blog posts), cannot be assigned categories/tags, and can be displayed in a hierarchical structure.
- Attachment (post_type: 'attachment') holds information about files (including images and related metadata) uploaded through the WordPress media upload system.
- Revision (post_type: 'revision') used to hold a draft post as well as any past revisions of a published post.
- Navigation menu (post_type: 'nav_menu_item') holds information on every single item in WordPress navigation menu system.
Custom Post Types
These are new post_type you can create. A custom post type must be added to WordPress via register_post_type() function: it defines the post_type’s specifics like labels, features, availability, etc.
Codex Tip: Note that you must call register_post_type() before the admin_menu and after the after_setup_theme action hooks. You can use the init hook here.
Here's a basic example of adding a custom post type:
add_action( 'init', 'create_post_type' );
function create_post_type() {
register_post_type( 'selsior_product',
array(
'labels' => array(
'name' => __( 'Products' ),
'singular_name' => __( 'Product' )
),
'public' => true,
'has_archive' => true,
)
);
}
Source
This creates a post_type named Product identified as selsior_product.
The register_post_type() function has two major arguments: labels define post_type name (in singular and plural forms); the public is a flag to show the post_type on admin screens and publicly (on the website), if it has archive, etc.
You can add more options to your first labels array to further detail and define it. Here’s a different example:
function custom_post_type() {
$labels = array(
'name' => _x( 'Movies', 'Post Type General Name', 'twentythirteen' ),
'singular_name' => _x( 'Movie', 'Post Type Singular Name', 'twentythirteen' ),
'menu_name' => __( 'Movies', 'twentythirteen' ),
'parent_item_colon' => __( 'Parent Movie', 'twentythirteen' ),
'all_items' => __( 'All Movies', 'twentythirteen' ),
'view_item' => __( 'View Movie', 'twentythirteen' ),
'add_new_item' => __( 'Add New Movie', 'twentythirteen' ),
'add_new' => __( 'Add New', 'twentythirteen' ),
'edit_item' => __( 'Edit Movie', 'twentythirteen' ),
'update_item' => __( 'Update Movie', 'twentythirteen' ),
'search_items' => __( 'Search Movie', 'twentythirteen' ),
'not_found' => __( 'Not Found', 'twentythirteen' ),
'not_found_in_trash' => __( 'Not found in Trash', 'twentythirteen' ),
);
$args = array(
'label' => __( 'movies', 'twentythirteen' ),
'description' => __( 'Movie news and reviews', 'twentythirteen' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'excerpt', 'author', 'thumbnail', 'comments', 'revisions', 'custom-fields', ),
'taxonomies' => array( 'genres' ),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'show_in_admin_bar' => true,
'menu_position' => 5,
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page',
);
register_post_type( 'movies', $args );
}
add_action( 'init', 'custom_post_type', 0 );
Source
Now we have created a post_type movies that will categorized in Genres and support configuration of excerpt, author, comments, custom-fields, etc. through post-editor.
Displaying your Custom Post Type
If you don’t create a custom template for your post type, WordPress will use default templates in your theme (archive.php and single.php) to show your new post types. If you stick with this road, you will need to update the permalink structure.
Go to Appearance >> Menus and add a new custom link to your menu (For e.g., products, movies). By default it should look something like this: www.your-website.com/?post_type=products
Write your own domain and replace the ‘products’ with your custom post type’s name.
You can also create a custom template to display your post type archive or singly. The easiest (and reasonable) way to do this would be copy everything from your theme’s archive.php into a new file named archive-{post_type name}.php and then start modifying it. You can do the same with your single-{post-type name}.php file.
There are also a few practices to keep in mind:
- In order to avoid breaking a site or losing the custom post types upon switching to another theme, it would be better to define custom post types as a plugin.
- Instead of generic custom post type name (as used in the examples above like ‘products’ or ‘movies’, go for prefixes with your post-type name. This minimizes the chances of conflict with post types created by another plugin or theme. Just don’t exist char length of 20 or it’ll become to huge to fit in the VARCHAR field, and don’t use wp_.
- Give your clients/admins/self some control over the post-type configuration by adding proper controls. For example, here’s how you edit the columns for your custom post type’s overview page:
function change_columns( $cols ) {
$cols = array(
'cb' => '<input type="checkbox" />',
'url' => __( 'URL', 'trans' ),
'referrer' => __( 'Referrer', 'trans' ),
'host' => __( 'Host', 'trans' ),
);
return $cols;
}
add_filter( "manage_<CPT>_posts_columns", "change_columns" );
Source
Querying by Post Type
You can create new queries via post_type argument of WP_Query in any template to display posts from specific post_type. Here’s an example of a loop to display 10 latest posts of type ‘product’ with their titles and content:
$args = array( 'post_type' => 'product', 'posts_per_page' => 10 );
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) : $loop->the_post();
the_title();
echo '<div class="entry-content">';
the_content();
echo '</div>';
endwhile;
Source: Codex
Custom Post Types in the Main Query
Creating and registering a custom post type does not mean it gets added to the main query on its own. To make your custom post type posts show up on default archives or the homepage (along with other custom and default post types), you need this code:
add_action( 'pre_get_posts', 'add_my_post_types_to_query' );
function add_my_post_types_to_query( $query ) {
if ( is_home() && $query->is_main_query() )
$query->set( 'post_type', array( 'post', 'page', 'movie' ) );
return $query;
}
Source: Codex
Key Takeaway
There are so many awesome things you can do now that you have a grasp on custom post types. Essentially, this is the way to fit WordPress to your needs with your own hands.
Just remember to check out the WordPress Codex and other resources, and if this is your first time fiddling with the code, make sure you are working with a child theme.