Permalink post_type structure with taxonomy slug in it
Table of Contents
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:
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; }