Ben Lobaugh Online

I think, therefore I am. I am, therefore I sail

WordPress: Add category to permalink and redirect old permalinks

Occasionally you run across a situation where a website permalink structure needs to be changed from what it has been to a new structure. Most sites that have been public will have links to them from other websites that need to properly redirect to the new permalink structure. Removing an element from the permalink structure and redirecting old links is trivial, however adding an additional element to the url structure can be difficult. For example:

A fairly typical structure is: /%year%/%month%/%day%/%postname%/

Lets use a simple example of adding the category to the permalink structure: /%category%/%year%/%month%/%day%/%postname%/

To provide a more useful example:

Original: https://ben.lobaugh.net/2016/05/15/cruising-the-san-juans
With category: https://ben.lobaugh.net/sailing/2016/05/15/cruising-the-san-juans

The original url is going to display the 404 page. I am going to provide an example that you can use in your own site to capture the url causing the 404 and attempt to locate the new post permalink. If none can be found it will fallback to the 404 page. As long as there is something in the original url you can use to locate a post (like the  %postname%) this method will work with minor tweaks.

add_action( 'template_redirect', 'maybe_redirect_404_old_permalink' );
/**
 * Attempts to forward old permalinks to the new permalink structure
 *
 * @author Ben Lobaugh
 */
function maybe_redirect_404_old_permalink() {
    // Only run this function if we are on a 404
    if( ! is_404() ) {
        return;
    }
 
    // "trick" to get the full URL
    $url = add_query_arg( '', '' );

    /*
     * Pull the URL path apart to find a slug (post_name)
     * The final segment should be the slug
     */
    $parts = explode( '/', $url );
    $parts = array_filter( $parts );
    $size = count( $parts );
    $maybe_slug = $parts[ $size ]; // We use size here because the filter turned 1 based

    // Attempt to locate corresponding post in the database
    $args = array(
        'name'        => $maybe_slug,
        'post_type'   => 'post',
        'post_status' => 'publish',
        'numberposts' => 1,
    );

    $posts = get_posts( $args );

    // Identify a found post
    if( $posts && ! empty( $posts[0]->ID ) ) {
        $post_id = $posts[0]->ID;

        $post_url = get_permalink( $post_id );

        // Attempt to forward to the new post permalink
        if( $post_url ) {
            wp_safe_redirect( $post_url, 301 ); // Permanent redirect
        }
    }

    /*
     * If we made it down here then we could not find a matching post in
     * the database. No biggie, simply do nothing and display the 404 page
     * as normal 🙂
     */
}

Previous

Bon Voyage Zippey

Next

WordPress Multisite: Global options that can be overridden per site

4 Comments

  1. Mischa

    This is exactly the use case I was searchin for. However after thinking this through I came to the conclussion to not make this redirects. I simply keep my old permalink structure, and won’t add the category. Since this approach will put performance at risk.

  2. Ahmad

    EXACTLY WHAT I NEEDED! Thanks a lot for this amazing piece of code. I think this should be an essential to have in wordpress. I was worried that changing urls might hurt my sites SEO but this saved me!

  3. Bob

    Spot on thank you 🙂

  4. Steve

    This is awesome – works like a charm. Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *

Powered by WordPress & Drip City Coffee