By default, WordPress comes with post and pages as the main content types. However, we can create our custom content type, and these custom content types are referred to as WordPress custom Post Types. In this article, we will create a custom post type and a template to display it.

Why you should use WordPress Custom Post type

Let’s take a look at a real-life example. We want to run a blog where we would publish movie reviews among posts. We would like to add features for every movie, make it searchable by actors’ names, genres, etc. This is when we will need to create a custom post type for movies and add custom taxonomy for genres and actors. You can make custom post type with a plugin or custom it in your theme functions.php file. 

If you are going with the plugin, the first thing you need to do is install and activate WordPress Custom Post Type UI plugin. Now go to CPT UI –> Add New to create a new custom post type.

WordPress Custom Post Type Kontra agency example

First, you need to provide your post slug/name which will be used as the link to your post archive (example.com/your_custom_post_slug). Next field will be your label name, and this will be visible in your WordPress dashboard.  The problem with using a plugin is that your custom post types will disappear when the plugin is deactivated. Any data you have in those custom post types will still be there, but your custom post type will be unregistered and will not be accessible from the admin area.

If you are making a custom theme or know how functions.php works, its best to make custom post types that way. Let’s take a look at a minimal working example to help you understand how it works.

// Our custom post type function
function create_posttype() {
 
    register_post_type( 'movies',
    // CPT Options
        array(
            'labels' => array(
                'name' => __( 'Movies' ),
                'singular_name' => __( 'Movie' )
            ),
            'public' => true,
            'has_archive' => true,
            'rewrite' => array('slug' => 'movies'),
        )
    );
}
// Hooking up our function to theme setup
add_action( 'init', 'create_posttype' );

What this code does is registering a post type ‘movies’ with an array of arguments. Arguments consist of all of our options for a custom post type. This array consists of two parts: the first which is labels and the second part which includes options like public visibility, has an archive, and slug that will be used in URLs for this post type

// Register Custom Post Type function kontra_custom_post_type() { $labels = array( 'name' => _x( 'Movies', 'Post Type General Name', 'text_domain' ), 'singular_name' => _x( 'Movie', 'Post Type Singular Name', 'text_domain' ), 'menu_name' => __( 'Movies', 'text_domain' ), 'name_admin_bar' => __( 'Movies', 'text_domain' ), 'archives' => __( 'Movie Archives', 'text_domain' ), 'attributes' => __( 'Movie Attributes', 'text_domain' ), 'parent_item_colon' => __( 'Parent Item:', 'text_domain' ), 'all_items' => __( 'All Movies', 'text_domain' ), 'add_new_item' => __( 'Add New Movie', 'text_domain' ), 'add_new' => __( 'Add New Movie', 'text_domain' ), 'new_item' => __( 'New Movie', 'text_domain' ), 'edit_item' => __( 'Edit Movie', 'text_domain' ), 'update_item' => __( 'Update Movie', 'text_domain' ), 'view_item' => __( 'View Movies', 'text_domain' ), 'view_items' => __( 'View Movies', 'text_domain' ), 'search_items' => __( 'Search Movies', 'text_domain' ), 'not_found' => __( 'Not found Movie', 'text_domain' ), 'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ), 'featured_image' => __( 'Featured Image', 'text_domain' ), 'set_featured_image' => __( 'Set featured image', 'text_domain' ), 'remove_featured_image' => __( 'Remove featured image', 'text_domain' ), 'use_featured_image' => __( 'Use as featured image', 'text_domain' ), 'insert_into_item' => __( 'Insert into Movie', 'text_domain' ), 'uploaded_to_this_item' => __( 'Uploaded to this Movie', 'text_domain' ), 'items_list' => __( 'Movie list', 'text_domain' ), 'items_list_navigation' => __( 'Movies list navigation', 'text_domain' ), 'filter_items_list' => __( 'Filter Movies list', 'text_domain' ), ); $rewrite = array( 'slug' => 'movies', 'with_front' => true, 'pages' => true, 'feeds' => true, ); $args = array( 'label' => __( 'Movie', 'text_domain' ), 'description' => __( 'Custom post type made for Kontra agency blog example', 'text_domain' ), 'labels' => $labels, 'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ), 'taxonomies' => array( 'genere', ' actors' ), 'hierarchical' => false, 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 5, 'menu_icon' => 'dashicons-editor-video', 'show_in_admin_bar' => true, 'show_in_nav_menus' => true, 'can_export' => true, 'has_archive' => 'Movies', 'exclude_from_search' => false, 'publicly_queryable' => true, 'rewrite' => $rewrite, 'capability_type' => 'page', ); register_post_type( 'movies', $args ); } add_action( 'init', 'kontra_custom_post_type', 0 );

Now when you’ve registered your WordPress custom post type, you should have it visible in your admin menu. Lastly, besides using custom fields which we already learned, the only thing left is to register taxonomies genre and actors.

// Register Custom Taxonomy function kontra_custom_taxonomy() { $labels = array( 'name' => _x( 'Genres', 'Taxonomy General Name', 'text_domain' ), 'singular_name' => _x( 'Genre', 'Taxonomy Singular Name', 'text_domain' ), 'menu_name' => __( 'Genre', 'text_domain' ), 'all_items' => __( 'All Generes', 'text_domain' ), 'parent_item' => __( 'Parent Genre', 'text_domain' ), 'parent_item_colon' => __( 'Parent Genre:', 'text_domain' ), 'new_item_name' => __( 'New Genre Name', 'text_domain' ), 'add_new_item' => __( 'Add New Genre', 'text_domain' ), 'edit_item' => __( 'Edit Genre', 'text_domain' ), 'update_item' => __( 'Update Genre', 'text_domain' ), 'view_item' => __( 'View Genre', 'text_domain' ), 'separate_items_with_commas' => __( 'Separate Genres with commas', 'text_domain' ), 'add_or_remove_items' => __( 'Add or remove Genres', 'text_domain' ), 'choose_from_most_used' => __( 'Choose from the most used', 'text_domain' ), 'popular_items' => __( 'Popular Genres', 'text_domain' ), 'search_items' => __( 'Search Genres', 'text_domain' ), 'not_found' => __( 'Not Found', 'text_domain' ), 'no_terms' => __( 'No Genres', 'text_domain' ), 'items_list' => __( 'Genres list', 'text_domain' ), 'items_list_navigation' => __( 'Genres list navigation', 'text_domain' ), ); $args = array( 'labels' => $labels, 'hierarchical' => true, 'public' => true, 'show_ui' => true, 'show_admin_column' => true, 'show_in_nav_menus' => true, 'show_tagcloud' => true, ); register_taxonomy( 'genre', array( 'movies' ), $args ); } add_action( 'init', 'kontra_custom_taxonomy', 0 );

Display Custom post type

WordPress comes with built-in support for displaying your custom post types. Once you have added a few items into your new custom post type, it is time to display them on your website. You can preview your archive of custom posts with archive link registered (example.com/movies/). If you don’t have a custom template named archive-movies.php, WordPress will use the default archive.php template. And to showcase your new custom post, you can make a single-movies.php template which can also be additionally customized. If you want to query posts form your custom post type in any of the templates, you can do it by calling WP_Query.

// WP_Query arguments $args = array( 'post_type' => array( 'movies' ), 'nopaging' => false, 'posts_per_page' => '10', 'order' => 'ASC', 'orderby' => 'date', ); // The Query $query = new WP_Query( $args ); // The Loop if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); // do something } } else { // no posts found } // Restore original Post Data wp_reset_postdata();

These custom queries come really handy when you start creating custom templates. Hope that even if this seems difficult to you, give custom posts a try with a plugin and when you figure out all the setting start with coding it in functions.php



Author

Ivan Jurković

Front-end web developer at Kontra and a car enthusiast.