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
- http://example.com/books/{book-post-slug}
- http://example.com/movies/{movie-post-slug}
- http://example.com/games/{game-post-slug}
Taxonomy Term archive URLs
- http://example.com/genres/{genre-term-slug}
(this archive will include all post types, which is not what we’re after)
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:
http://example.com/books/book-genres/{genre-term-slug}
(lists only books associated with the specified genre-term-slug )http://example.com/movies/movie-genres/{genre-term-slug}
(lists only movies associated with the specified genre-term-slug )http://example.com/games/game-genres/{genre-term-slug}
(lists only games associated with the specified genre-term-slug )