SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30 SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30 SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30
SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30 SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30 SPECIAL OFFER Get 30% OFF on All Qode Plugins Discount code: WOOCOMMERCE30
BACK TO TOP

What Are Custom Post Types in WordPress and How to Make Them

What Are Custom Post Types in WordPress and How to Make Them

WordPress has long become more than just a simple blogging platform. Over the years, it has grown to become a bona fide Content Management System, with a vast array of options allowing for great flexibility and scalability. Custom post types are among those possibilities, and today we’re going to explain what they are, what they can be used for, and how to use them.

What Are WordPress Custom Post Types

When thinking about WordPress posts, most people think of blog posts or articles, image galleries, video, and audio posts. All these, plus quote and link posts, are actually formats of just one post type that comes with WordPress by default – the “actual,” regular posts.

There are five default post types in WordPress:

  • Posts
  • Pages
  • Attachments
  • Revisions
  • Navigation menus

The official WordPress support site has a detailed explanation for each of these default types, but right now we want to focus on custom post types, i.e. those post types that do not fall into any of these categories and which you have to make by yourself if you need them.

When it comes to blogging, the default WordPress content types are usually enough. But since WordPress started to power all sorts of websites, a need for new post types has emerged.

For instance, if you’re running a portfolio website, you may need a Portfolio custom post type. Businesses and companies need Testimonials, online shops need Products, real estate websites need Property post types, and so on.

Now, depending on your theme, these custom posts may already come out of the box. For instance, your listing WordPress theme will most likely include Listing items, and these are actually custom post types, created by the theme developer.

Eiddo Sample
Eiddo Sample
Eiddo Sample

When to Use Custom Post Types in WordPress

In theory, you can always just use a default WordPress post type – a “regular” post or a page – to publish your content and organize it using tags and categories. But this is not always convenient, especially for niche-related custom content types mentioned above (portfolio items, properties, etc.), or at least it’s not always a great idea.

For instance, there are situations when you want to publish something using the default post types but they just don’t seem to fit your content right. This is the case with coupons, for example. Or, you may find it difficult to classify and organize the post in question using default WordPress taxonomies (that’s why there are custom taxonomies as well, but we’ll get to that in a bit). Also, there are post types that cannot and should not be displayed chronologically, in your blog roll.

Let’s take an online entertainment magazine. A large portion of the magazine’s content output consists of news. For these, the magazine will most likely use regular WordPress posts. But the magazine also has a section where they review new albums, movies, restaurants, and so on. These can, theoretically, be published as “plain” posts, but in order to add extra features to them (star rating system, search by author, etc), the magazine will most likely have to create a custom post type for reviews.

In simple words – not all post types fall into one of the default categories. And that’s precisely when you need custom post types.

How to make a custom post type in WordPress

The process of making custom post types involves different things, so we divided it into four different sections, each with its own detailed explanation. These sections describe the various steps you need to take to properly make a functioning custom post type for your site. As this topic is quite advanced, i.e. it involves coding, you might need to do additional research alongside the explanations we provided if you find yourself stuck.

Before we dive in, we recommend brushing up on your knowledge of FTP, as some steps include editing the WordPress files on the server or uploading new ones. Making a backup of your website is also a good precautionary measure since we will be adding new code to our website.

Registering the custom post type

The first step to creating your custom post type is to register it. And we will cover two ways of performing the registration process, so users of different experience levels can pick the one that suits them. The first involves using a plugin. With it, you can select all the properties the custom post type will have through an intuitive plugin user interface. The second involves using code. It is more complex but it also gives you more freedom for customizing the new post template.

  • Using a WordPress plugin

Using a WordPress plugin to add a new feature to a website is very beginner-friendly. This makes it the preferred choice of many WordPress users. For this article, we will show you how to register a custom post template using the Custom Post Types UI (CPT UI) plugin. This plugin is by far the most popular plugin for creating your own custom post types. It is free and can be used even by those who don’t know much about coding or advanced WordPress customization.

The plugin allows you to use the WordPress interface to register and manage custom post types, and it also helps you create custom taxonomies. This part is particularly important since your website might need additional taxonomies for your posts, not just categories and tags. For example, if you’re running a book website, you can add custom taxonomies for sorting the content by author, genre, location, and so on.

To add a post type, just follow these simple steps:

After downloading and installing the plugin, you will notice a new option in your WordPress Admin Dashboard, called CPT UI. From there, simply click on Add/Edit Post Types.

Add Edit Post Types

Under the Add New Post Type tab, assign a slug to your post type and add plural and singular labels for it.

CPT UI Basic Settings

For example, if your post type is going to be related to the movie industry, the slug should say “movie,” the plural label should be “Movies” and the singular label will be “Movie”. You can see how that looked on our end from the screenshot above. Also, when you’re picking your labels, make sure to capitalize them.

At this point, you can register the custom post type by clicking on the Add Post Type button. Your brand new post type will now appear as a separate option in your Dashboard menu.

Dashboard Movies

You can also choose to make additional settings to your custom post type before you register it.

Below the Basic settings, you will see a number of fields for entering Additional labels.

Additional Labels

You can fill these in to change the names of default WordPress fields for post management and publishing – basically create custom fields – or you can leave them at default.

The section below contains more custom post type settings.

Settings

Here, you can set everything from visibility and searchability of your custom post type to menu position, supported post type options, and taxonomies. After you set everything you want, make sure to register your new custom post type by pressing the Add Post Type button at the bottom, if you haven’t done so already.

You can always edit your custom post types later on, by clicking on the Edit Post Types tab and selecting the post type you want to edit or delete.

Edit Post Types

Next to the Edit Post Types tab, you’ll see two more—View Post Types and Import/Export Post Types.

The View Post Types tab allows you to see details for all the custom post types you have registered.

View Post Types

The most important information in the View Post Types tab is located at the end, under Template Hierarchy. It shows the hierarchical approach that WordPress uses for determining which template file is responsible for displaying the content of particular pages and posts.

The CPT UI plugin displays the hierarchy for the custom post type single post, its archive, and a page belonging to a custom taxonomy once that taxonomy has been registered. As such, it displays the potential names you can use for the files you’ll be creating. This is a topic we will return to later in the article.

Finally, you can use the Import/Export Post Types tab (or the CPT UI > Tools option in the menu) to migrate (import or export) your registered custom post types.

Custom Post Type
  • Using custom code

Using code to register your custom single post types constitutes a more technical approach than working with a plugin. Therefore, it can be interesting to intermediate and advanced WordPress users and those who want to learn more. To help explain how the process works, we created an example for this article.

There are two key points to the registration process. One is the use of the register_post_type() function with the appropriate parameters. The other is the use of an appropriate hook that you can hook that code onto. You can see in the Description section of the register_post_type() function page that the post type registration shouldn’t be hooked to a hook before init. Therefore, we will use the init action hook as the earliest suitable one. That means a possible pseudocode example would look like this:

function your-function-name () {
// Insert the register_post_type() function call here.
}
add_action( 'init' , 'your-function-name' );

However, for this article, we decided to use a marginally more complex approach to registering custom post types. We will be registering our custom post type using object-oriented programming, i.e. within a custom-defined class object. Of course, the same two key points still apply—the use of the register_post_type() function and hooks. Both will be implemented within that class using the methods, i.e. functions, belonging to that class.

Although slightly more difficult to implement, the benefit of this approach is that the code is easier to read as key things are compartmentalized using class methods. Furthermore, it is easier to scale up a well-structured code by adding new layers of functionalities like taxonomies, shortcodes, or widgets later on. In the example we’ll show you, we added the taxonomies.

Once you create the proper class structure, the code you put together can be re-used as a blueprint, with minimal edits, to make other custom post types. Before we jump into the coding example prepared for this article, we have to mention where you can put this code. Namely, code like this should be inserted inside the functions.php file of your child theme or a site-specific plugin.

class Movie {
private static $instance;
public function __construct() {
$this->movieBase = 'movie';
add_action( 'init', array( $this, 'registerMoviePostType' ) );
}
public static function get_instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
self::$instance = new self();
}
return self::$instance;
}
public function registerMoviePostType() {
register_post_type( $this->movieBase,
array(
'labels' => array(
'name' => esc_html__( 'Movies', 'your-translate-domain' ),
'singular_name' => esc_html__( 'Movie', 'your-translate-domain' ),
'add_item' => esc_html__( 'New Movie', 'your-translate-domain' ),
'add_new_item' => esc_html__( 'Add New Movie', 'your-translate-domain' ),
'edit_item' => esc_html__( 'Edit Movie', 'your-translate-domain' )
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => $this->movieBase ),
'menu_position' => 5,
'show_ui' => true,
'show_in_rest' => true,
'supports' => array(
'author',
'title',
'editor',
'thumbnail',
'excerpt',
'page-attributes',
'comments',
'custom-fields'
)
)
);
}
}
Movie::get_instance();

Let’s break this code down.

Simply put, the code represents a custom class called Movie with one property called $instance and three methods: __construct(), get_instance(), and registerMoviePostType().

The get_instance() method is used to instantiate an instance of the Movie class, which is done at the end of the code by calling that method. As such, the main part of the code lies in the other two class methods, i.e. functions.

The __construct() method is the constructor method for the Movie class and it only has two lines of code. The first defines a property named movieBase, which is, in fact, the slug of the custom post type we wish to register. Technically speaking, this line of code isn’t necessary, we included it for the sake of convenience. As the post slug often needs to be repeated in various functions, having it stored into one variable makes the process of changing it later on easier.

As for the second line, it represents the use of the add_action() function where we hooked the registerMoviePostType() method to the init action hook. This hook is the earliest hook that can be used for registering custom post types. And for everything to work properly, we used the register_post_type() function to register our movie post type.

When registering the movie post type, we specified the following attributes: an array of labels that display in the backend, made the movie post type publicly available in the frontend, enabled the archive page, enabled permalink rewrites, and set the slug to ‘movie’. We also enabled the user interface for movie handling in the admin area and set its position to 5, which will ensure that the section will be placed higher up in the admin’s left side menu, right below Posts.

Moreover, we enabled the REST API for this custom post type, which is one of the two requirements for enabling Gutenberg as the editor for this custom post type. Then, using the supports property, we enabled the title, author, the option to insert content using the editor (which is the second requirement for Gutenberg), a featured image, and the option to insert an excerpt. We also enabled the Page (i.e. Post) Attributes section that displays the menu order field, as well as the comment section and the option to use custom fields within this post type.

Besides the ones we mentioned above, there are a lot more parameters you can use. You can get an idea of what those are from the previous section by examining the CPT UI plugin options. To get a more precise overview of all the currently available parameters, you should review the detailed information section from the register_post_type() function page.

Registering a custom taxonomy

After registering a custom post type, the next thing you should do is register the taxonomies that are associated with it. Taxonomies allow you to group various post items, giving structure to your content while helping your visitors find what they want easily. Most users are very familiar with categories and tags, which are the two default WordPress taxonomies. However, you might feel like these two won’t represent your custom post type well. In that case, registering a custom taxonomy is the best solution.

As part of this article, we will show you how to register a movie genre as a custom taxonomy. Given that we’re creating a movie custom post type, a taxonomy that matches the niche would be a perfect fit. Much as we did in the previous section, we will show you how this process goes using either the CPT UI plugin or custom code.

  • Using a WordPress plugin

Adding taxonomies with the CPT UI plugin is quite easy, you just need to follow the steps below.

Navigate to the CPT UI admin section and click on Add/Edit Taxonomies. Then, under the Add New Taxonomy tab, assign a taxonomy slug and plural and singular labels. The slug should use Latin and alphanumeric characters, and the labels should be capitalized. Also, please note that the plugin won’t allow you to use a dash—it will be replaced with an underscore instead.

Once you fill in those fields, make sure to tick the checkbox next to the label of the custom post type that you want to tie this taxonomy to. In our case, that was the Movies label.

After that, you can opt to register the custom taxonomy by clicking on Add Taxonomy.

Adding Taxonomy

Then, the new taxonomy will appear in the Dashboard menu, under the Movies section that it’s tied to.

Movies Genres

However, there are additional settings you can add to your taxonomy before registering it, as you can see in the section below.

Underneath the Basic settings, you will see a number of fields for entering Additional labels.

Taxonomy Additional Labels

The section below it, titled Settings, allows you to set the parameters for your custom taxonomy.

Taxonomy Settings

And, if you haven’t done so already, make sure to register the taxonomy by pressing the Add Taxonomy button at the bottom.

As we mentioned in our section on registering post types, you can always make edits later on. In this case, you can do so by clicking on the Edit Taxonomies tab and selecting the taxonomy you want to edit or delete.

Edit Taxonomy

Also, as before, you’ll have two extra tabs available. The View Taxonomies tab allows you to see details for all the taxonomies you have registered. To locate the information on a registered taxonomy, you need to scroll down to the Taxonomies section and find the appropriate taxonomy within the list of those currently registered.

Taxonomies

The Template Hierarchy column is present here as well, and it provides information on the template hierarchy for taxonomy files. Meaning, it contains potential file names you can use for the file that will help you display the content of a given taxonomy.

Finally, you can use the Import/Export Taxonomies tab (or the CPT UI > Tools option in the menu) to migrate (import or export) your registered taxonomies.

Custom Post Type Taxonomies
  • Using custom code

Similar to registering a custom post type with code, the process of registering custom taxonomies relies on the two key points. One is the use of the register_taxonomy() function with appropriate parameters within the code, and the other is the process of hooking the code onto an appropriate action hook. As earlier, you shouldn’t use any hook that is loaded before the init hook. Thus, the pseudocode for this process would look this:

function your-function-name () {
// Insert the register_taxonomy() function call here.
}
add_action( 'init' , 'your-function-name' );

However, earlier we decided to use object-oriented programming for this article, and we will stick to that now by using our movie custom post type example, i.e. the Movie class object. Therefore, we only need to update the existing code with additional properties and methods that help us register our movie genre taxonomy and tie it to the movie custom post type. We also need to include the two key points, which enable this process, in our additional code.

With that in mind, we created the updated version of the earlier code, which registers both the movie post type and movie genre taxonomy within the Movie class object. This code, much like its first version, should be inserted inside the functions.php file of your child theme or a site-specific plugin. Having said that, you can find the new code snippet below.

class Movie {
private static $instance;
public function __construct() {
$this->movieBase = 'movie';
$this->genreBase = 'movie_genre';
add_action( 'init' , array( $this, 'registerMoviePostType' ));
add_action( 'init' , array( $this, 'registerMovieGenreTax' ));
}
public static function get_instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
self::$instance = new self();
}
return self::$instance;
}
public function registerMoviePostType() {
register_post_type( $this->movieBase,
array(
'labels' => array(
'name' => esc_html__( 'Movies', 'your-translate-domain' ),
'singular_name' => esc_html__( 'Movie', 'your-translate-domain' ),
'add_item' => esc_html__( 'New Movie', 'your-translate-domain' ),
'add_new_item' => esc_html__( 'Add New Movie', 'your-translate-domain' ),
'edit_item' => esc_html__( 'Edit Movie', 'your-translate-domain' )
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => $this->movieBase ),
'menu_position' => 5,
'show_ui' => true,
'show_in_rest' => true,
'supports' => array(
'author',
'title',
'editor',
'thumbnail',
'excerpt',
'page-attributes',
'comments',
'custom-fields'
)
)
);
}
public function registerMovieGenreTax() {
$genre_labels = array(
'name' => esc_html__( 'Genres', 'your-translate-domain' ),
'singular_name' => esc_html__( 'Genre', 'your-translate-domain' ),
'search_items' => esc_html__( 'Genres', 'your-translate-domain' ),
'all_items' => esc_html__( 'Genres', 'your-translate-domain' ),
'parent_item' => esc_html__( 'Parent Genre', 'your-translate-domain' ),
'parent_item_colon' => esc_html__( 'Parent Genres:', 'your-translate-domain' ),
'edit_item' => esc_html__( 'Edit Genre', 'your-translate-domain' ),
'update_item' => esc_html__( 'Update Genre', 'your-translate-domain' ),
'add_new_item' => esc_html__( 'Add New Genre', 'your-translate-domain' ),
'new_item_name' => esc_html__( 'New Genre', 'your-translate-domain' ),
'menu_name' => esc_html__( 'Genres', 'your-translate-domain' ),
);
register_taxonomy( $this->genreBase, array( $this->movieBase ), array(
'hierarchical' => true,
'labels' => $genre_labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_rest' => true
) );
}
}
Movie::get_instance();

If you compare this snippet to the previous one, you’ll notice we added three parts to the code. Those three new parts enable us to register the movie genre as a taxonomy related to the movie custom post type.

In this snippet, we stored the taxonomy slug movie_genre in a property called genreBase for the sake of convenience. Then, we created a function called registerMovieGenreTax(), which is responsible for registering our custom taxonomy. This is done by calling the register_taxonomy() function.

The movie genre taxonomy is defined with the movie_genre taxonomy slug and it’s tied to the movie post type alone because we specified the movie slug as the second argument. Besides that, we defined an array of additional arguments relating to the properties this taxonomy will have. These include being hierarchical (i.e. able to define subgenres), using previously defined labels, having a user interface for managing genres, having an admin column on the associated post type (movie), and exposing the taxonomy to the REST API.

For the registration process to work properly, we hooked the registerMovieGenreTax() function to the init action hook by adding the extra line to the __construct() method within the Movie class.

Finally, as with the previous example, there are additional arguments we could have used. You can see what those are in the CPT UI plugin section. And, to learn more about all the available arguments the register_taxonomy() function can take, you can review the function’s additional information section.

Adding custom post items

After registering the movie custom post type and corresponding movie genre taxonomy, the next step is to create the items that would belong to them. This is done using the WordPress user interface and the Movies section located in the admin dashboard. Before we start, make sure that you set the show_ui property to true while registering the post type and taxonomy as it’s a requirement for this step. After that, you can proceed.

To add a new movie item, navigate to the Movies section that you created and select the Add New option.

Movies Add New

Next, you will be given various sections which you can fill with information relevant to that movie item. As part of this article, we created the example you can see below. We framed the sections that we used in red. They include the title, editor, genre selection, featured image, excerpt, and two custom fields—qode-movie-cast and qode-movie-release-date—that we defined and filled with relevant information.

Used Sections
Used Sections

In the same way, you can add your content to any already defined custom post type. And, please note, you will only be able to edit the sections that you previously defined using the supports property.

There are two more points we have to mention.

Firstly, to assign a movie genre to a specific movie item, you will need to create the genre items beforehand from the Movies > Genres > Add New Genre section. This is done by inserting the name of the genre at a minimum and clicking on the Add New Genre button.

Add New Genre

Secondly, if you can’t see the Custom Fields section even though it’s enabled for your custom post type, make sure that the panel is set to be displayed within the Preferences section. To do that, you need to open the edit screen, click on the three dots in the top right corner, and select the Preferences option from the menu that opens.

Preferences

This will open the Preferences popup window. Within it, click on the Panels tab and locate the Custom fields option from the Additional section. Make sure the option for Custom fields is enabled so they can show on your end.

Preferences Panels

Finally, after adding your content, make sure to publish the custom post item by pressing the Publish button in the top right corner. This brings us to the last and most important step in the process of making custom post types—displaying the content we added to our custom post items.

Displaying custom post items

To display the movie items that we already created and populated with content, we first have to cover a very important concept—the Template Hierarchy. We mentioned it briefly earlier in the article, but taking a closer look at it now will help us understand which template file is responsible for displaying the content of the corresponding post type.

  • Understanding Template Hierarchy

The template file hierarchy for custom post types is as follows:

– custom page/post template file

– single-{post-type}-{post-slug}.php

– single-{post-type}.php

– single.php

– singular.php

– index.php

The {post-type} notation represents the slug of a specific post type, default or custom. The {post-slug} notation represents the slug of a specific post item belonging to that post type. The {post-slug} is used if you want to create a template file that displays only the content of a specific custom post item, and not the content of all items belonging to that post type.

For our movie custom post type, which has the word movie as its slug, the same template hierarchy be:

– custom page/post template file

– single-movie-{post-slug}.php

– single-movie.php

– single.php

– singular.php

– index.php

To determine which template file to use for displaying our movie item(s), WordPress needs to go down this list, inspecting which of the files exist on your server. Then, after figuring out which of the files exist, the highest in the hierarchy will be used for displaying the movie item content. Therefore, to display a custom post type item, you need to create a suitable template file with appropriate code that adheres to this template hierarchy. Then, you should upload this file to the server in a location we will discuss shortly.

To clarify, you need to create an appropriate template file because otherwise, WordPress will use one of the fallback files that exist within your theme (single.php, singular.php, or index.php). If that happens, any custom content you might have inserted (for example, movie genres, cast members, release date, etc.) won’t be shown as there will be no code within those files for displaying it. And the layout provided by an alternative file might not suit your tastes or be appropriate for that custom post type.

For this article, we prepared a custom template file that will help us display the content we inserted. You can follow our example or you can create one of the other two possible files, which are single-{post-type}-{post-slug}.php and single-{post-type}.php. We are creating a custom page/post template file as it will give us the most flexibility, given that it is at the top of the custom post type template hierarchy.

With that being said, we have to mention that the plugin we’ve used for the registration process, CPT UI, won’t be used for this last step. While CPT UI has a premium version that helps you display content, it does so by using a custom shortcode. Since that’s not the direction we want to go in this article, we will focus on the code we have prepared.

As you can see from the list above, the template hierarchy for custom post types has a very strict naming convention for all files except the first one. Since the WordPress 4.7 update, users have been able to create custom page templates that can be applied for posts or custom post types. These files would be located at the top of the corresponding template file hierarchy.

The only requirement for making a custom page template is having a properly defined template header. To clarify, a template header is a simple comment that specifies the template name and other important information regarding the custom template. For example, if you create a custom template for posts or custom post types, like our movie, you will need to specify the post types that this template will be available for. In that case, your template header would look something like this:

<?php
/*
Template Name: Full-Width Layout
Template Post Type: post, page, movie
*/

Using this example template header, we can register a custom page or post template called Full-Width Layout that could be used for pages, posts, and movies. In this way, you can register a custom template for any post type or an array of post types.

However, just using a template header isn’t all—you will still need to add the code responsible for displaying content. Then, both the code and the corresponding template header should be placed in a separate file and uploaded to the server to a location where custom page/post templates are recognized. As the whole process is best described using an example, we created a template file specifically for this article.

  • Custom template file example

In this section, you can find the code we put together to create our custom template file. This file will help us display all the various pieces of content that we inserted in the previous steps.

<?php
/*
* Template Name: Movie Single Layout
* Template Post Type: movie
*/
get_header();
?>
<main id="qodef-page-content"
class="qodef-layout--template"
role="main">
<div class="qodef-grid-inner">
<div class="qodef-grid-item">
<div class="qodef-movie-holder">
<?php
if ( have_posts() ) {
while ( have_posts() ) :
the_post(); ?>
<article <?php post_class( 'qodef-movie-item' ); ?>>
<div class="qodef-e-inner">
<?php if ( has_post_thumbnail() ) { ?>
<div class="qodef-e-media">
<div class="qodef-featured-image">
<?php $item_id = get_the_ID();
echo get_the_post_thumbnail( $item_id, 'large' ); ?>
</div>
</div>
<?php } ?>
<div class="qodef-e-content">
<div class="qodef-e-content-inner">
<div class="qodef-e-content-text"><?php the_content(); ?></div>
<div class="qodef-e-info-items">
<?php
$movie_genre = get_the_terms( get_the_ID(), 'movie_genre' );
if ( ! empty( $movie_genre ) && ! is_wp_error( $movie_genre ) ) { ?>
<h5 class="qodef-e-genre"><?php echo esc_html__( 'Genre: ', 'your-translate-domain' ); ?>
<span class="qodef-e-genre-values">
<?php foreach ( $movie_genre as $genre_value ) { ?>
<a itemprop="url" class="qodef-e-genre-link"
href="<?php echo esc_url( get_term_link( $genre_value->term_id ) ); ?>"><?php echo esc_html( $genre_value->name ); ?></a>
<?php } ?>
</span>
</h5>
<?php } ?>
<?php $movie_cast = get_post_meta( $item_id, 'qode-movie-cast', true );
// Check if the custom field has a value.
if ( ! empty( $movie_cast ) ) { ?>
<h5 class="qodef-e-cast"><?php esc_html_e( 'Top cast: ', 'your-translate-domain' ); ?>
<span class="qodef-e-cast-value"><?php echo esc_html( $movie_cast ); ?></span>
</h5>
<?php } ?>
<?php $movie_release_date = get_post_meta( $item_id, 'qode-movie-release-date', true );
// Check if the custom field has a value.
if ( ! empty( $movie_release_date ) ) { ?>
<h5 class="qodef-e-release-date"><?php esc_html_e( 'Release date: ', 'your-translate-domain' ); ?>
<span class="qodef-e-release-date-value"><?php echo esc_html( $movie_release_date ); ?></span>
</h5>
<?php } ?>
</div>
</div>
</div>
</div>
<div class="qodef-e-navigation">
<?php
$post_navigation = array(
'prev' => array(
'label' => '<span class="qodef-e-navigation-item-label">' . esc_html__( '< Prev', 'your-translate-domain' ) . '</span>'
),
'next' => array(
'label' => '<span class="qodef-e-navigation-item-label">' . esc_html__( 'Next >', 'your-translate-domain' ) . '</span>'
),
);
if ( get_adjacent_post( false, '', true ) !== '' ) {
$post_navigation['prev']['post'] = get_adjacent_post( false, '', true );
}
if ( get_adjacent_post( false, '', false ) !== '' ) {
$post_navigation['next']['post'] = get_adjacent_post( false, '', false );
}
foreach ( $post_navigation as $key => $value ) {
if ( isset( $post_navigation[ $key ]['post'] ) ) {
$current_post = $value['post'];
$post_id = isset( $value['post_id'] ) && ! empty( $value['post_id'] ) ? $value['post_id'] : $current_post->ID;
?>
<a itemprop="url"
class="qodef-e-navigation-item qodef--<?php echo esc_attr( $key ); ?>"
href="<?php echo esc_url( get_permalink( $post_id ) ); ?>">
<?php
if ( ! empty( $value['label'] ) ) {
echo wp_kses( $value['label'], array( 'span' => array( 'class' => true ) ) );
}
?>
</a>
<?php } ?>
<?php } ?>
</div>
</article>
<?php endwhile; // End of the loop.
} else { ?>
<p class="qodef-m-posts-not-found"><?php esc_html_e( 'No posts were found for provided query parameters.', 'your-translate-domain' ); ?></p>
<?php }
wp_reset_postdata();
?>
</div>
</div>
</div>
</main>
<?php
get_footer();

Let’s take a closer look at this code. To make it easier to grasp, we’ll examine it in a simplified form.

From the template header at the top, you can see the code represents a template called Movie Single Layout, which is only applicable to our movie custom post type. After the template header, we have the main part of the code. You can find a pared-down version of it below.

The header and footer templates defined within your current theme are displayed using the get_header() and get_footer() functions. Besides that, we can see an HTML structure that contains a simple WordPress Loop. If the conditions of that loop are met, then the rest of the code is executed. Otherwise, a message stating “No posts were found for provided query parameters.” will be shown. Additionally, we restored the $post global variable to the current post in the main query using the wp_reset_postdata() function.

<?php
/*
* Template Name: Movie Single Layout
* Template Post Type: movie
*/
get_header();
?>
<main id="qodef-page-content"
class="qodef-layout--template"
role="main">
<div class="qodef-grid-inner">
<div class="qodef-grid-item">
<div class="qodef-movie-holder">
<?php
if ( have_posts() ) {
while ( have_posts() ) :
the_post(); ?>
<!-- Some code goes here-->
<?php endwhile; // End of the loop.
} else { ?>
<p class="qodef-m-posts-not-found"><?php esc_html_e( 'No posts were found for provided query parameters.', 'your-translate-domain' ); ?></p>
<?php }
wp_reset_postdata();
?>
</div>
</div>
</div>
</main>
<?php
get_footer();

Looking at the code located within the WordPress Loop, we can see it represents the content, stored within the <article> HTML tag, that each movie item will display. We added a list of WordPress-defined CSS classes, to the <article> element alongside our qodef-movie-item by using the post_class() function. We can use these CSS classes later for creating any CSS code that might be needed for stylization.

As for the content, the <article> contains three distinct parts. First, as long as a featured image is added to the custom post item’s content, it will be displayed. This is accomplished thanks to the get_the_post_thumbnail() function, and the image will be shown in the predefined ‘large’ image size.

The second part can be divided into two smaller parts. Those are the content added to the editor and displayed thanks to the_content() function, and the information items that we added to the movie item. In our example, these are the genres to which the movie belongs, its top cast, and scheduled release date. Incidentally, these are the two custom fields that we defined previously.

Finally, the third part displays the navigation. This will be the previous/next post navigation, which allows visitors to navigate through your movie post items.

<article <?php post_class( 'qodef-movie-item' ); ?>>
<div class="qodef-e-inner">
<?php if ( has_post_thumbnail() ) { ?>
<div class="qodef-e-media">
<div class="qodef-featured-image">
<?php $item_id = get_the_ID();
echo get_the_post_thumbnail( $item_id, 'large' ); ?>
</div>
</div>
<?php } ?>
<div class="qodef-e-content">
<div class="qodef-e-content-inner">
<div class="qodef-e-content-text"><?php the_content(); ?></div>
<div class="qodef-e-info-items">
<!-- Some code goes here-->
</div>
</div>
</div>
</div>
<div class="qodef-e-navigation">
<!-- Some code goes here-->
</div>
</article>

We’ll briefly explain the last two sections. The movie genres that the movie is associated with are displayed by using the get_the_terms() function. And they are linked to the corresponding genre taxonomy page through the use of the get_term_link() function.

The qode-movie-cast and qode-movie-release-date custom fields are displayed by using the get_post_meta() function. Additionally, all three properties—the genre values, movie cast, and release date—are displayed only if they are added to the post in the preceding step. This is achieved by wrapping each coding section with a corresponding if statement.

<div class="qodef-e-info-items">
<?php
$movie_genre = get_the_terms( get_the_ID(), 'movie_genre' );
if ( ! empty( $movie_genre ) && ! is_wp_error( $movie_genre ) ) { ?>
<h5 class="qodef-e-genre"><?php echo esc_html__( 'Genre: ', 'your-translate-domain' ); ?>
<span class="qodef-e-genre-values">
<?php foreach ( $movie_genre as $genre_value ) { ?>
<a itemprop="url" class="qodef-e-genre-link"
href="<?php echo esc_url( get_term_link( $genre_value->term_id ) ); ?>"><?php echo esc_html( $genre_value->name ); ?></a>
<?php } ?>
</span>
</h5>
<?php } ?>
<?php $movie_cast = get_post_meta( $item_id, 'qode-movie-cast', true );
// Check if the custom field has a value.
if ( ! empty( $movie_cast ) ) { ?>
<h5 class="qodef-e-cast"><?php esc_html_e( 'Top cast: ', 'your-translate-domain' ); ?>
<span class="qodef-e-cast-value"><?php echo esc_html( $movie_cast ); ?></span>
</h5>
<?php } ?>
<?php $movie_release_date = get_post_meta( $item_id, 'qode-movie-release-date', true );
// Check if the custom field has a value.
if ( ! empty( $movie_release_date ) ) { ?>
<h5 class="qodef-e-release-date"><?php esc_html_e( 'Release date: ', 'your-translate-domain' ); ?>
<span class="qodef-e-release-date-value"><?php echo esc_html( $movie_release_date ); ?></span>
</h5>
<?php } ?>
</div>

As for the navigation, it is displayed thanks to the get_adjacent_post() function. We also haven’t filtered the items based on a specific taxonomy or excluded any from being queried. Meaning, the navigation will go through all the movies based on the publishing date, in descending order.

Furthermore, we defined the labels as < Prev and Next > and placed them inside the $post_navigation variable, which is an associative array. Then, using a foreach loop, the appropriate navigation is displayed. It’s worth mentioning that the navigation labels have been sanitized with the wp_kses() function, which is just one of the several functions that we used in code for sanitization purposes.

<div class="qodef-e-navigation">
<?php
$post_navigation = array(
'prev' => array(
'label' => '<span class="qodef-e-navigation-item-label">' . esc_html__( '< Prev', 'your-translate-domain' ) . '</span>'
),
'next' => array(
'label' => '<span class="qodef-e-navigation-item-label">' . esc_html__( 'Next >', 'your-translate-domain' ) . '</span>'
),
);
if ( get_adjacent_post( false, '', true ) !== '' ) {
$post_navigation['prev']['post'] = get_adjacent_post( false, '', true );
}
if ( get_adjacent_post( false, '', false ) !== '' ) {
$post_navigation['next']['post'] = get_adjacent_post( false, '', false );
}
foreach ( $post_navigation as $key => $value ) {
if ( isset( $post_navigation[ $key ]['post'] ) ) {
$current_post = $value['post'];
$post_id = isset( $value['post_id'] ) && ! empty( $value['post_id'] ) ? $value['post_id'] : $current_post->ID;
?>
<a itemprop="url"
class="qodef-e-navigation-item qodef--<?php echo esc_attr( $key ); ?>"
href="<?php echo esc_url( get_permalink( $post_id ) ); ?>">
<?php
if ( ! empty( $value['label'] ) ) {
echo wp_kses( $value['label'], array( 'span' => array( 'class' => true ) ) );
}
?>
</a>
<?php } ?>
<?php } ?>
</div>

This wraps up our explanation of the code responsible for displaying the content of our movie custom post type item.

  • Additional steps

After creating the code for your custom template file, you should save it as a .php file and upload it to your server using FTP. You should place the file on your server into one of the four appropriate locations for custom template files. These are the active parent theme directory, active child theme directory, or a subdirectory within either of those two locations.

As an additional piece of advice, you should take special care when naming the file. Avoid using any of the already reserved names (e.g. index, archive, page, single,…) or names that contain an already reserved prefix (e.g. single-, archive-, page-,…). We recommend naming the file the same as the template, so you can keep track of it easily. You can also upload the file into a newly-created subdirectory within your active theme to keep everything neatly ordered.

However, if you opted to create template files that are slightly lower in the template hierarchy (i.e. single-{post-type}-{post-slug}.php or single-{post-type}.php), then there are only two locations where you can upload them. These are the directories of your parent or child theme, depending on which you are using.

Once you upload the template file, a new section named Template will appear in the backend of the movie single item. Generally speaking, this section doesn’t need to be included using the supports property—it will appear if there is at least one possible template to choose from. As such, to display the content that you added to your movie item, you will need to edit it and assign the template that you created earlier to the page or post. This is done from the Template section, located in the top right corner of the movie edit screen. Once you’ve selected your template, press the Update button to update the movie item.

Choose Template and Update

Don’t forget, you will need to select the template for each new movie item that you create. And, if you created multiple templates, then you will be able to pick which one you want to use. After all that, you should examine the output of your custom post item (in our case, movie item) from the frontend.

While selecting the proper template will ensure that all your content, including custom fields, meta boxes, and other custom properties, will be displayed, it doesn’t mean you’ll be happy with the stylization. Your content might only be partially stylized by the CSS properties that are currently found on your website. To fix any stylization issues, you will need to create additional CSS code.

This CSS code needs to be created on a case-by-case basis as it relies on various factors, such as the existing HTML structure of the page and the CSS rules that are already present on the website. As such, you will need to create this CSS code on your own. But to help you, we will share the CSS we created to style our example for our article. It was created for our website specifically, so you shouldn’t copy-paste it without modification.

.qodef-e-inner {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.qodef-e-media{
margin-bottom: 20px;
}
.qodef-e-media, .qodef-e-content {
display: flex;
flex-direction: column;
flex-basis: 100%;
}
@media screen and (min-width: 1025px) {
.qodef-e-media, .qodef-e-content{
flex: 1;
}
.qodef-featured-image{
padding-right: 15px;
}
.qodef-e-content-inner {
padding-left: 15px;
}
}
.qodef-e-info-items h5 {
color: #000;
font-weight: 600;
}
.qodef-e-genre-link:not(:last-of-type):after{
content: ',';
}
.qodef-e-navigation {
display: flex;
justify-content: space-between;
margin-top: 40px;
color: #000;
font-weight: 600;
font-size: 18px;
}

After you create the CSS for your website, you need to know where to safely place it. For most WordPress users we suggest using the default WordPress location for storing CSS—the Appearance > Customize > Additional CSS section. Make sure to properly comment on it, so that it stands out from any other CSS code you may have added before. However, more advanced WordPress users can put the CSS code they create into a separate file, upload it to the server, and enqueue it using the wp_enqueue_style() function. For more instructions on how to do that, you can take a look at our article on enqueueing custom scripts and stylesheets.

Finally, once you complete all the steps we previously described, it is time to examine the display of your custom post type item. You can see how our custom movie item turned out in the screenshot below.

Result

Final Thoughts

Post types in WordPress allow you to publish various types of content and structure it differently so it stands out from the rest. However, WordPress webmasters often stick to the five default WordPress post types and find themselves constrained. This is often the case when the website being created has a niche, or highly specific, purpose or content. This makes creating custom post types the obvious solution.

In this article, we discussed all the various steps that need to be taken to successfully make a custom post type. These include registering the custom post type and the taxonomy, or taxonomies, that are tied to it alongside the appropriate properties they possess. To do this, you can choose between a WordPress plugin and using code.

After adding content to our custom post type items, we also discussed what it takes to display these items on the website. While this process may be more challenging, we covered it in detail while touching on a deeper concept called the WordPress Template Hierarchy. Given all of this, we are confident you will be able to reproduce all these steps on your own and make a custom post type perfect for your website while gaining a deeper understanding of how WordPress works.

Post your comment

Comments0