Permalink post_type structure with taxonomy slug in it

Let’s look at the simple example of a eshop on WordPress.

The shop has custom post type product and category for products product_cat. By default product links looks like /product/product-name/.

How to change it to /{product category name}/{product name}/?

For example we want to change /product/burton-cartel/ to /snowboard-bindings/burton-cartel/.

The following code will let you do that. The code is for your current theme functions.php file and works great with the following permalink settings:

Recomended permalink settings for the code.

Rewrite Post type links and Change default request

add_filter('post_link', 'rudr_post_type_permalink', 20, 3);
add_filter('post_type_link', 'rudr_post_type_permalink', 20, 3);

function rudr_post_type_permalink($permalink, $post_id, $leavename) {

	$post_type_name = 'product'; // post type name, you can find it in admin area or in register_post_type() function
	$post_type_slug = 'product'; // the part of your product URLs, not always matches with the post type name
	$tax_name = 'product_cat'; // the product categories taxonomy name

	$post = get_post( $post_id );

	if ( strpos( $permalink, $post_type_slug ) === FALSE || $post->post_type != $post_type_name ) // do not make changes if the post has different type or its URL doesn't contain the given post type slug
		return $permalink;

        $terms = wp_get_object_terms( $post->ID, $tax_name ); // get all terms (product categories) of this post (product)


        if ( !is_wp_error( $terms ) && !empty( $terms ) && is_object( $terms[0] ) ) // rewrite only if this product has categories
        	$permalink = str_replace( $post_type_slug, $terms[0]->slug, $permalink );

	return $permalink;
}


add_filter('request', 'rudr_post_type_request', 1, 1 );

function rudr_post_type_request( $query ){
	global $wpdb;
	
	$post_type_name = 'product'; // specify your own here
	$tax_name = 'product_cat'; // and here
	
	$slug = $query['attachment']; // when we change the post type link, WordPress thinks that these are attachment pages

	// get the post with the given type and slug from the database
	$post_id = $wpdb->get_var(
		"
		SELECT ID
		FROM $wpdb->posts
		WHERE post_name = '$slug'
		AND post_type = '$post_type_name'
		"
	);
	
	$terms = wp_get_object_terms( $post_id, $tax_name ); // our post should have the terms
	
	
	if( isset( $slug ) && $post_id && !is_wp_error( $terms ) && !empty( $terms ) ) : // change the query
	
		unset( $query['attachment'] );
		$query[$post_type_name] = $slug;
		$query['post_type'] = $post_type_name;
		$query['name'] = $slug;
		
	endif;
	
	return $query;
}

How to perform 301 redirect from the old post URLs

add_action('template_redirect', 'rudr_post_type_redirect');

function rudr_post_type_redirect() {
	
	$post_type_name = 'product'; // specify your own here
	$post_type_slug = 'product'; // here
	$tax_name = 'platform'; // and here
	
	if( strpos( $_SERVER['REQUEST_URI'], $post_type_slug ) === FALSE) // do not redirect if the URL doesn't contain the given post type slug
		return;

	if( is_singular( $post_type_name ) ) : // if post type page
		global $post, $wp_rewrite;
		
		$terms = wp_get_object_terms( $post->ID, $tax_name ); // get terms attached
		
		if ( !is_wp_error( $terms ) && !empty( $terms ) && is_object( $terms[0] ) ) :
			
			wp_redirect( site_url() . '/' . $wp_rewrite->front . '/' . $terms[0]->slug . '/' . $post->post_name, 301 );
			// wp_redirect( get_permalink( $post->ID ), 301 ); // depends on the previous code from this post

			exit();
        	endif;
	endif;

}
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on Twitter