Taxonomy archives based on Custom Post Type

Here is a complete example made possible using add_rewrite_rule(). The basic setup for this example is documented first, then we’ll get to the real part of the solution using add_rewrite_rule().

Taxonomy and Post Type registration

Register the genre taxonomy and the book, movie, and game post types (note that the singular version of each of these names is being used in this example because it’s considered a best practice).

// Create taxonomy: genre for post types: book, movie, and game
// https://codex.wordpress.org/Function_Reference/register_taxonomy
add_action( 'init', 'wpse247013_register_taxonomies', 0 );
function wpse247013_register_taxonomies() {
    $args = [
        'public'            => true,
        'hierarchical'      => false,
        'label'             => __( 'Genres', 'textdomain' ),
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => 'genre',
        'rewrite'           => [ 'slug' => 'genres' ],
    ];

    register_taxonomy( 'genre', [ 'book', 'movie', 'game' ], $args );
}

// Create post types: movie, book, and game
// https://developer.wordpress.org/reference/functions/register_post_type/
add_action( 'init', 'wpse247013_register_post_types' );
function wpse247013_register_post_types() {
    $book_args = [
        'label'              => __( 'Books', 'textdomain' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => [ 'slug' => 'books' ],
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => null,
        'supports'           => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ],
        'taxonomies'         => [ 'genre' ],
    ];
    register_post_type( 'book', $book_args );

    $movie_args = [
        'label'             => __( 'Movies', 'textdomain' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => [ 'slug' => 'movies' ],
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => null,
        'supports'           => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ],
        'taxonomies'         => [ 'genre' ],
    ];
    register_post_type( 'movie', $movie_args );

    $game_args = [
        'label'             => __( 'Games', 'textdomain' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => [ 'slug' => 'games' ],
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => null,
        'supports'           => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ],
        'taxonomies'         => [ 'genre' ],
    ];
    register_post_type( 'game', $game_args );
}

Basic URL Examples

Based on the setup above, we’ll get these URLs out of the box:

Post type archive URLs

Single post type URLs

Taxonomy Term archive URLs

Handle rewrite rules

Rewrite rules must be added in order to limit a particular genre term to a single post type. An additional rule is needed for pagination for each post type.

/**
 * Add rewrite rules for genre terms limited to book, movie, and game post types.
 * Pagination issue fix via http://wordpress.stackexchange.com/a/23155/2807
 * @link https://codex.wordpress.org/Rewrite_API/add_rewrite_rule
 */
function wpse247013_rewrite_rules() {
    // Book Genres
    add_rewrite_rule( '^books/book-genres/([^/]+)/?$',
            'index.php?taxonomy=genre&post_type=book&term=$matches[1]', 'top' );

    // Book Genres pagination
    add_rewrite_rule( '^books/book-genres/([^/]+)/page/([0-9]+)?$',
            'index.php?post_type=book&genre=$matches[1]&paged=$matches[2]', 'top' );

    // Movie Genres
    add_rewrite_rule( '^movies/movie-genres/([^/]+)/?$',
            'index.php?taxonomy=genre&post_type=movie&term=$matches[1]', 'top' );

    // Movie Genres pagination
    add_rewrite_rule( '^movies/movie-genres/([^/]+)/page/([0-9]+)?$',
            'index.php?post_type=movie&genre=$matches[1]&paged=$matches[2]', 'top' );

    // Game Genres
    add_rewrite_rule( '^games/game-genres/([^/]+)/?$',
            'index.php?taxonomy=genre&post_type=game&term=$matches[1]', 'top' );

    // Game Genres pagination
    add_rewrite_rule( '^games/game-genres/([^/]+)/page/([0-9]+)?$',
            'index.php?post_type=game&genre=$matches[1]&paged=$matches[2]', 'top' );
}
add_action( 'init', 'wpse247013_rewrite_rules', 10, 0 );

Make sure to flush the rewrite rules by visiting Settings > Permalinks after adding this code to your plugin or theme. If you’re using a plugin, you can flush the rules programmatically using register_activation_hook.

Custom URLs

The rewrite rules added above will enable the following new URLs:

Leave a Comment