25
votes

I'm trying to create a custom permalink structure that will allow me to accomplish the following.

  1. I have a custom post type called "projects"
  2. I have a custom taxonomy called "project-category" that is assigned to the CPT "projects"

I want my permalink structure to look like this:

projects/category/project-name

or

/%custom-post-type%/%custom-taxonomy%/%post-name%/

I've been able to succesfully use /%category%/ in permalinks for normal, out-of-the-box WP posts, but not for CPTs.

How would creating such a permalink structure affect the URLs or other pages? Is it possible de define a custom permalink structure and restrict it to a single CPT?

Thanks

2
Would this plugin solve your issue? wordpress.org/plugins/custom-post-type-permalinksWill Thresher
I'm always hesitant to use too many plugins, but I'll definetely give it a try! Thank you.Bruno Cloutier
I totally agree with you, although lately I've been a bit more lenient with really basic plugins that are essentially just taking some grunt work away from us. Hope that works out for you!Will Thresher
Like Advanced Custom Fields - the greatest WP plugin of all time as far as I'm concerned.Will Thresher
Indeed, ACF is an absolute must for any WordPress install! CPTP works wonderfuly, thanks for the tip!Bruno Cloutier

2 Answers

31
votes

Lucky for you, I just had to do this for a client project. I used this answer on the WordPress Stackexchange as a guide:

/**
 * Tell WordPress how to interpret our project URL structure
 *
 * @param array $rules Existing rewrite rules
 * @return array
 */
function so23698827_add_rewrite_rules( $rules ) {
  $new = array();
  $new['projects/([^/]+)/(.+)/?$'] = 'index.php?cpt_project=$matches[2]';
  $new['projects/(.+)/?$'] = 'index.php?cpt_project_category=$matches[1]';

  return array_merge( $new, $rules ); // Ensure our rules come first
}
add_filter( 'rewrite_rules_array', 'so23698827_add_rewrite_rules' );

/**
 * Handle the '%project_category%' URL placeholder
 *
 * @param str $link The link to the post
 * @param WP_Post object $post The post object
 * @return str
 */
function so23698827_filter_post_type_link( $link, $post ) {
  if ( $post->post_type == 'cpt_project' ) {
    if ( $cats = get_the_terms( $post->ID, 'cpt_project_category' ) ) {
      $link = str_replace( '%project_category%', current( $cats )->slug, $link );
    }
  }
  return $link;
}
add_filter( 'post_type_link', 'so23698827_filter_post_type_link', 10, 2 );

When registering the custom post type and taxonomy, be sure to use the following settings:

// Used for registering cpt_project custom post type
$post_type_args = array(
  'rewrite' => array(
    'slug' => 'projects/%project_category%',
    'with_front' => true
  )
);

// Some of the args being passed to register_taxonomy() for 'cpt_project_category'
$taxonomy_args = array(
  'rewrite' => array(
    'slug' => 'projects',
    'with_front' => true
  )
);

Of course, be sure to flush rewrite rules when you're done. Good luck!

0
votes

Since WordPress changed a lot in the recent years there is a new solution for this.

// Used for registering cpt_project custom post type
$post_type_args = array(
  'rewrite' => array(
    'slug' => '/%custom-post-type%/%custom-taxonomy%/%postname%/',
    'with_front' => true
    'walk_dirs' => false
  )
);

%custom-post-type% must match the name for your custom post type %custom-taxonomy% must match the name for your taxonomy that WordPress automatically creates the right rewrite rules and links

with 'walk_dirs' => false you prevent WP from creating crazy rules like with only [^/]+/ cause your link starts with a custom-post-type

and often this dir walk isn't even needed cause u access only the sites in your structure or separate taxonomy sites.

With this your rewrite rules are as precise as possible, and you don't need to fetch the rules with

add_filter( 'rewrite_rules_array', 'so23698827_add_rewrite_rules' );

and prepend them later on with

add_filter( 'post_type_link', 'so23698827_filter_post_type_link', 10, 2 );

as mentioned in the accepted answer. This saves memory and execution time!

Hope this helps anyone, who is searching for this Problem with WP Versions > 5.X