I’m working on a project with several custom post types, where one of the post types is naturally (but not actually in the WordPress sense) the ‘parent’ of the others. For example, say I have a custom post type called ‘book’ and another one called ‘character’, and say I want to have the following custom URL structure:
books/[book-name]/characters/[character-name]
Thus, the canonical URL for the post about the character Thing One in the book The Cat In the Hat would be:
books/cat-in-the-hat/characters/thing-one
A character is associated with a book with a post meta row.
I’ve created custom rewrites to make it work…
add_rewrite_rule (
'^books/([^/]+)/characters/([^/]+)/?$',
'index.php?book=$matches[1]&character=$matches[2]&post_type=character'
);
…but I’d also need to make sure that the ‘character post’ in the URL belongs to the ‘book’…
books/cat-in-the-hat/characters/thing-one //good
books/anna-karenina/characters/count-vronsky //good
books/anna-karenina/characters/thing-one //bad
This can’t happen automatically: I have to hook into a filter or an action to check whether the character matches the book, and if not, either redirect to the correct, canonical URL or force a 404.
Where’s the best place to accomplish this check? My choices (so far) are:
- The
request
filter — i.e. checking before the main query is instantiated. - The various WP_Query filters — i.e. make WP_Query do the checking for me by adding a post meta constraint.
I realize this is rather an open-ended question and probably a matter of taste, but any insight into the most code and database efficient way of doing things would be appreciated. Thanks.
I tackled subordinate post types with a similar vein but was not as strict on the canonical url aspect; however, I ran into a similar situation with deeplinking custom WooCommerce product type links.
I leveraged the
template_redirect
hook to push a 404 if it didn’t match the route and thepost_type_link
to ensure the meta tag for canonical link and allthe_permalink()
andget_permalink()
references matched the intended link.A snippit of the way I would recommend handling this:
Modified from source