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 :)
*/
}