2

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;
});
5
  • What I've tried: among other things I tried a variation on this but the post links without a category still 404 wordpress.stackexchange.com/a/133587/170714
    – Dennis
    Commented Jun 27, 2019 at 0:13
  • I'm sure there's a way to do this, but maybe it would worth considering just removing those top-level categories and moving the posts into the sub-categories? if "News" is already in the URL path, and presumably all the posts in that multisite sub-site are News related, why keep it as a category at all? You could move posts by briefly using a plugin like this: wordpress.org/plugins/bulk-move
    – Michelle
    Commented Jul 1, 2019 at 20:40
  • I'd considered that but some things are too broad or don't fit neatly into a sub-category — I've set the top-level category ("News") as the default category (replacing "uncategorized"), which is why I've been looking for parallel questions related to removing "/uncategorized/" from the permalink, but none of those solutions seem to work.
    – Dennis
    Commented Jul 1, 2019 at 20:58
  • I've played around and I think the problem is the /%postname%/ type permalink you're wanting for the News category (ie no category slug included) is what's used for WordPress "Pages", so WP isn't finding the single Post correctly. I'm not sure how to get around that. Another workaround (not what you want I know) would be to change just the slug of the News category to be something like 'general' or 'all' or 'stories'... not ideal based on your question but probably more future-proof than messing with WP core behavior.
    – Michelle
    Commented Jul 1, 2019 at 23:57
  • I think that's what's happening too, but if you have your permalink structure set to just /%postname%/, it doesn't differentiate, site.com/page-1 and site.com/post-1 both work. I get what you're saying about changing the category slugs, and I can do that as a last-ish resort / temporary fix but the site's been around for a long time with the category structure I'd mentioned.
    – Dennis
    Commented Jul 2, 2019 at 4:35

1 Answer 1

2

I think, there are two things to be resolved:

1) You need that posts (and other post types) also followed that command, so use both:

add_filter( 'post_link',      'remove_parent_category', 10, 3 );
add_filter( 'post_type_link', 'remove_parent_category', 10, 3 ); 

2) but then categories also need to have changed url, so use:

add_filter( 'category_link', 'remove_parent_category_2', 10, 2);
function remove_parent_category_2($termlink, $term_id )
{
    return implode("/", array_unique(explode("/", $termlink)));
}

after that, go to SETTINGS > PERMALINKS > SAVE and see if it works.

btw, what I suggest, is (after backup of database) just to remove those categories from categories at all (from WP dashboard > categories), and WP will do everything itself, without need for custom codes.

2
  • Still testing but this appears to have done the trick, thank you very much for your help!
    – Dennis
    Commented Jul 8, 2019 at 1:42
  • I think I figured out the missing piece, can you check out my update and let me know what you think?
    – Dennis
    Commented Jul 8, 2019 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.