I had a site with a url structure like
example.com/news/story-1
example.com/news/local/story-2
example.com/food/tacos/story-3
Now it's been moves to a multisite, with /news/ and /food/ as the site subfolders.
Within those sub-sites, the top level category is News or Food, respectively. So if the permalink is set to /%category%/%postname%/
, the permalink come out as
example.com/news/news/local/story-title
example.com/food/food/tacos/story-title
In order to remove the category that's a duplicate of the site subfolder, I added this to functions.php:
add_filter( 'post_link', 'remove_parent_category', 10, 3 );
function remove_parent_category( $permalink, $post, $leavename ) {
$permalink_array = explode("/", $permalink);
$clean_permalink = array_unique($permalink_array);
$new_permalink = implode("/", $clean_permalink);
return $new_permalink;
}
This works, giving me back
example.com/news/local/story-2
example.com/food/tacos/story-3
and the ability to navigate to
example.com/news/
example.com/news/local
etc.
However posts that are in the top level category now 404 (which I suppose is to be expected) because their url is now
example.com/news/story-1
(which is what I want).
From what I'm gathering I think I need to use either pre_post_link or a rewrite rule to treat those top level posts as if my permalink structure is just %postname%/
, but that's where I'm stuck.
UPDATE: Solved (I think?)
T.Todua's answer below got me started:
- posts in child categories work
- category and sub-category archives work
- pages work
but posts that had their last category stripped (posts in the top-most category) were still 404ing.
After much poking around with WP Rewrite and discovering that the way I thought I'd be able to solve this would not work, it did lead me down another path.
It turns out these "naked" posts with no category were hitting the category rule in WP Rewrite — so the query ended up being category_name = this-is-the-slug-for-a-post
. There was no way (at least that I could figure out) to prevent this in WP Rewrite, since there's no way to tell the difference between a category slug and a post slug using RegEx.
(Though I am wondering how, if your permalink structure is just %postname%/
, Wordpress differentiates between a page and a post. If anyone knows, I'd be interested.)
From there I found this post and modified it to my needs. It catches category requests and checks if there's a real category with that name. If not, it converts it to a post query.
The whole thing is below: please let me know if you see any major red flags or problems down the road with this approach.
add_filter('post_link','remove_parent_category', 10, 3 );
add_filter('post_type_link','remove_parent_category', 10, 3 );
add_filter('category_link','remove_parent_category', 10, 2);
function remove_parent_category($termlink, $term_id )
{
return implode("/", array_unique(explode("/", $termlink)));
}
add_filter('request', function(array $query_vars) {
// do nothing in wp-admin
if(is_admin()) {
return $query_vars;
}
// if the query is for a category
if(isset($query_vars['category_name'])) {
$query_cat = $query_vars['category_name'];
// if it's a real category do nothing
if(term_exists($query_cat, "category")) {
return $query_vars;
};
// save the slug
$postname = $query_vars['category_name'];
// completely replace the query with a post query
$query_vars = array('name' => "$postname");
}
return $query_vars;
});