Custom Post Type with Custom Taxonomies in WordPress

By default, WordPress allows you to publish two types of articles: Posts and Pages. When you write a typical article, you usually publish it as a “post”. When you publish contents about your website — like your website’s “About Us” or “Contact Us” page — you publish it as a “page”. If you use WordPress for a blog, it is the Posts that will be shown in your articles list, not the Pages. But what if you want another type of content for your website? You will need to create a custom post type.

The default taxonomies (Categories and Tags) in WordPress do not automatically link to custom post types. So, we recommend that you also create custom taxonomies for your custom post types.

Open the functions.php file of your WordPress website and paste all the lines of code below in it — make sure you edit them to suit your needs:

Creating a Custom Post Type

/*
* Creating a Custom Post Type: tutorials
*/
 
function custom_post_type_tutorials() {
 
// Set UI labels for Custom Post Type
    $labels = array(
        'name'                => _x( 'Tutorials', 'Post Type General Name' ),
        'singular_name'       => _x( 'Tutorial', 'Post Type Singular Name' ),
        'menu_name'           => __( 'Tutorials' ),
        'parent_item_colon'   => __( 'Parent Tutorial' ),
        'all_items'           => __( 'All Tutorials' ),
        'view_item'           => __( 'View Tutorial' ),
        'add_new_item'        => __( 'Add New Tutorial' ),
        'add_new'             => __( 'Add New' ),
        'edit_item'           => __( 'Edit Tutorial' ),
        'update_item'         => __( 'Update Tutorial' ),
        'search_items'        => __( 'Search Tutorial' ),
        'not_found'           => __( 'Not Found' ),
        'not_found_in_trash'  => __( 'Not found in Trash' ),
    );
     
// Set other options for Custom Post Type
     
    $args = array(
        'label'               => __( 'tutorials' ),
        'description'         => __( 'Tutorial news and reviews' ),
        'labels'              => $labels,
        // Features this CPT supports in Post Editor
        'supports'            => array( 'title', 'editor', 'excerpt', 'author', 'thumbnail', 'comments', 'revisions', 'custom-fields', ),
        // You can associate this CPT with a taxonomy or custom taxonomy. 
        'taxonomies'          => array( 'genres' ),
        /* A hierarchical CPT is like Pages and can have
        * Parent and child items. A non-hierarchical CPT
        * is like Posts.
        */ 
        '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',
    );
     
    // Registering your Custom Post Type
    register_post_type( 'tutorials', $args );
 
}
 
/* Hook into the 'init' action so that the function
* Containing our post type registration is not 
* unnecessarily executed. 
*/
 
add_action( 'init', 'custom_post_type_tutorials', 0 );

Creating Custom “Categories”

// Create a hierarchical taxonomy like categories: Tut Categories
add_action( 'init', 'create_tutorial_category_taxonomy', 0 );
function create_tutorial_category_taxonomy() {
    $labels = array(
        'name'              => _x( 'Tut Categories', 'taxonomy general name', 'textdomain' ),
        'singular_name'     => _x( 'Tut Category', 'taxonomy singular name', 'textdomain' ),
        'search_items'      => __( 'Search Tut Categories', 'textdomain' ),
        'all_items'         => __( 'All Tut Categories', 'textdomain' ),
        'parent_item'       => __( 'Parent Tut Category', 'textdomain' ),
        'parent_item_colon' => __( 'Parent Tut Category:', 'textdomain' ),
        'edit_item'         => __( 'Edit Tut Category', 'textdomain' ),
        'update_item'       => __( 'Update Tut Category', 'textdomain' ),
        'add_new_item'      => __( 'Add New Tut Category', 'textdomain' ),
        'new_item_name'     => __( 'New Tut Category Name', 'textdomain' ),
        'menu_name'         => __( 'Tut Categories', 'textdomain' ),
    );
    $args = array(
        'hierarchical'      => true,
        'labels'            => $labels,
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'tut-category', 'with_front' => false ),
    );
    register_taxonomy( 'tut-category', 'tutorials', $args);
}

Creating Custom “Tags”

// Create non-hierarchical taxonomy like tags: Tut Tags
add_action( 'init', 'create_tutorial_tag_taxonomy', 0 );
function create_tutorial_tag_taxonomy() {
    $labels = array(
        'name'                       => _x( 'Tut Tags', 'taxonomy general name', 'textdomain' ),
        'singular_name'              => _x( 'Tut Tag', 'taxonomy singular name', 'textdomain' ),
        'search_items'               => __( 'Search Tut Tags', 'textdomain' ),
        'popular_items'              => __( 'Popular Tut Tags', 'textdomain' ),
        'all_items'                  => __( 'All Tut Tags', 'textdomain' ),
        'parent_item'                => null,
        'parent_item_colon'          => null,
        'edit_item'                  => __( 'Edit Tut Tag', 'textdomain' ),
        'update_item'                => __( 'Update Tut Tag', 'textdomain' ),
        'add_new_item'               => __( 'Add New Tut Tag', 'textdomain' ),
        'new_item_name'              => __( 'New Tut Tag Name', 'textdomain' ),
        'separate_items_with_commas' => __( 'Separate tut tags with commas', 'textdomain' ),
        'add_or_remove_items'        => __( 'Add or remove tut tags', 'textdomain' ),
        'choose_from_most_used'      => __( 'Choose from the most used tut tags', 'textdomain' ),
        'not_found'                  => __( 'No tut tags found.', 'textdomain' ),
        'menu_name'                  => __( 'Tut Tags', 'textdomain' ),
    );
    $args = array(
        'hierarchical'          => false,
        'labels'                => $labels,
        'show_ui'               => true,
        'show_admin_column'     => true,
        'update_count_callback' => '_update_post_term_count',
        'query_var'             => true,
        'rewrite'               => array( 'slug' => 'tut-tag' ),
    );
    register_taxonomy( 'tut-tag', 'tutorials', $args);
}

Removing Custom Post Type Slug from Article URLs

/**
 * Remove the CPT base slug from CPT permalinks
 */
function wp_remove_cpt_slug( $post_link, $post ) {
    if ( 'tutorials' === $post->post_type && 'publish' === $post->post_status ) {
        $post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
    }
    return $post_link;
}
add_filter( 'post_type_link', 'wp_remove_cpt_slug', 10, 2 );
function wp_add_cpt_post_names_to_main_query( $query ) {
    // Bail if this is not the main query.
    if ( ! $query->is_main_query() ) {
        return;
    }
    // Bail if this query doesn't match our very specific rewrite rule.
    if ( ! isset( $query->query['page'] ) || 2 !== count( $query->query ) ) {
        return;
    }
    // Bail if we're not querying based on the post name.
    if ( empty( $query->query['name'] ) ) {
        return;
    }
    // Add CPT to the list of post types WP will include when it queries based on the post name.
    $query->set( 'post_type', array( 'post', 'page', 'tutorials' ) );
}
add_action( 'pre_get_posts', 'wp_add_cpt_post_names_to_main_query' );

Final Step

Go to Settings > Permalinks and choose Post name in the Common Settings section. Click on the Save Changes button.