I created a ‘forum’ taxonomy, using these rules:
register_taxonomy(
'forum',
array('topic'),
array(
'public' => true,
'name' => _a('Forums'),
'singular_name' => _a('Forum'),
'show_ui' => true,
'show_in_nav_menus' => true,
'hierarchical' => true,
'labels' => array(
'name' => _a('Forums'),
'singular_name' => _a('Forum'),
'search_items' => _a('Search Forums'),
'popular_items' => _a('Popular Forums'),
'all_items' => _a('All Forums'),
'parent_item' => _a('Parent Forum'),
'parent_item_colon' => _a('Parent Forum:'),
'edit_item' => _a('Edit Forum'),
'update_item' => _a('Update Forum'),
'add_new_item' => _a('Add New Forum'),
'new_item_name' => _a('New Forum Name'),
),
'query_var' => true,
'rewrite' => array('slug' => 'forums', 'with_front' => false, 'hierarchical' => true),
)
);
In the front-end the URLs looks like:
forums/general-discussion/sub-forum
How can I remove the front slug (“forums”)? Ie, change the URLs to:
general-discussion/sub-forum
If I pass a empty slug argument to register_taxonomy() it works, but that causes issues with the permalinks of the post type associated with this taxonomy
UPDATE
Since writing this WordPress core has added the
'do_parse_request'
hook that allows URL routing to be handled elegantly and without the need to extend theWP
class. I covered the topic in-depth in my 2014 Atlanta WordCamp talk entitled “Hardcore URL Routing“; the slides are available at the link.ORIGINAL ANSWER
URL Design has been important to be for well over a decade; I even wrote a blog about it several years back. And while WordPress is sum is a brilliant bit of software unfortunately it’s URL rewrite system is just short of brain dead (IMHO, of course. 🙂 Anyway, glad to see people caring about URL design!
The answer I’m going to provide is a plugin I’m calling
WP_Extended
that is a proof of concept for this proposal on Trac (Note that proposal started as one thing and evolved into another, so you have to read the entire thing to see where it was headed.)Basically the idea is to subclass the
WP
class, override theparse_request()
method, and then assign the global$wp
variable with an instance of the subclass. Then withinparse_request()
you actually inspect the path by path segment instead of using a list of regular expressions that must match the URL in their entirety.So to state it explicitly, this technique inserts logic in front of the
parse_request()
which checks for URL-to-RegEx matches and instead first looks for taxonomy term matches, but it ONLY replacesparse_request()
and leaves the entire rest of the WordPress URL routing system intact including and especially the use of the$query_vars
variable.For your use-case this implementation only compares URL path segments with taxonomy terms since that’s all you need. This implementation inspects taxonomy terms respecting parent-child term relationships and when it finds a match it assigns the URL path (minus leading and trailing slashes) to
$wp->query_vars['category_name']
,$wp->query_vars['tag']
or$wp->query_vars['taxonomy']
&$wp->query_vars['term']
and it bypasses theparse_request()
method of theWP
class.On the other hand if the URL path does not match a term from a taxonomy you’ve specified it delegates URL routing logic to the WordPress rewrite system by calling the
parse_request()
method of theWP
class.To use
WP_Extended
for your use-case you’ll need to call theregister_url_route()
function from within your theme’sfunctions.php
file like so:What here is the source code for the plugin:
P.S. CAVEAT #1
Although for a given site I think this technique works brilliantly but this technique should NEVER be used for a plugin to be distributed on WordPress.org for others to use. If it is at the core of a software package based on WordPress then that might be okay. Otherwise this technique should be limited to improving the URL routing for a specific site.
Why? Because only one plugin can use this technique. If two plugins try to use it they will conflict with each other.
As an aside this strategy can be expanded to generically handle practically every use-case pattern that could be required and that’s what I intend to implement as soon as I either find the spare time or a client who can sponsor the time that it would take to build fully generic implementations.
CAVEAT #2
I wrote this to override
parse_request()
which is a very large function, and it is quite possible that I missed a property or two of the global$wp
object that I should have set.. So if something acts wonky let me know and I’ll be happy to research it and revise the answer if need be.Anyway…
Simple, really.
Step 1: Stop using the rewrite parameter at all. We’re going to roll your own rewrites.
Step 2: Set verbose page rules. This forces normal Pages to have their own rules instead of being a catch-all at the bottom of the page.
Step 3: Create some rewrite rules to handle your use cases.
Step 4: Manually force a flush rules to happen. Easiest way: go to settings->permalink and click the save button. I prefer this over a plugin activation method for my own usage, since I can force the rules to flush whenever I change things around.
So, code time:
Remember that after adding this code, you need to have it active when you go flush the permalink rules (by Saving the page on Settings->Permalinks)!
After you’ve flushed the rules and saved to the database, then /whatever should go to your forum=whatever taxonomy page.
Rewrite rules really aren’t that difficult if you understand regular expressions. I use this code to help me when debugging them:
This way, I can see the current rules at a glance on my page. Just remember that given any URL, the system starts at the top of the rules and goes down through them until it finds one that matches. The match is then used to rewrite the query into a more normal looking ?key=value set. Those keys get parsed into what goes into the WP_Query object. Simple.
Edit: Side note, this method will probably only work if your normal custom post structure starts with something that isn’t a catchall, like %category% or some such thing like that. You need to start it with a static string or a numeric, like %year%. This is to prevent it catching your URL before it gets to your rules.
You will not be able to do this using WP_Rewrite alone, since it can’t distinguish between term slugs and post slugs.
You have to also hook into ‘request’ and prevent the 404, by setting the post query var instead of the taxonomy one.
Something like this:
Note that the taxonomy has to be defined before the post type.
This would be a good time to point out that having a taxonomy and a post type with the same query var is a Bad Idea.
Also, you won’t be able to reach posts that have the same slug as one of the terms.
I’d take a look at the code of the top level cats plugin:
http://fortes.com/projects/wordpress/top-level-cats/
You could easily adapt that so it’s looking for your custom taxonomy slug by changing the
on line 74 to something like like:
I’d suggest taking a look at the Custom Post Permalinks plugin. I don’t have time to test right now, but it may help with your situation.
Since I’m a familiar with your other question, I’ll answer with that in mind.
I haven’t tested this at all, but it might work if you execute this once right after you register all the permastructs you want.:
What this does: it removes the rewrite rules generated from the topics permalink from the normal flow of the rules array and re-merges them at the end of the array. This prevents those rules from interfering with any other rewrite rules. Next, it forces verbose rewrite rules (each page gets an individual rule with a specific regular expression). This prevents the pages from interfering with your topic’s rules. Finally, it executes a hard flush (make sure your .htaccess file is writable, otherwise this won’t work) and saves the very large very complicated array of rewrite rules.
There is a plugin for this.
It removes the type slug by adding a specific rule for each custom post type page.
Not sure if this will work for taxonomies, but it worked for custom post types
Although it hasn’t been updated for 2 years, the below plugin worked for me:
http://wordpress.org/plugins/remove-slug-from-custom-post-type/
FYI I’m running WP
3.9.1
with WP Types1.5.7
Use a slash as value for slug… 100% working