30

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

Bruno Cloutier
  • 1,003
  • 2
  • 10
  • 11

2 Answers2

33

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!

Community
  • 1
  • 1
Steve Grunwell
  • 896
  • 6
  • 21
  • Excellent, thank you! I was is a of a hurry so I had to rely on a plugin (which I prefer not to do) to do it quick. I'll implement this in future projects! – Bruno Cloutier May 20 '14 at 13:34
  • 1
    Have you ever tried to get this to work with sub-categories or know how I might get this to work with sub-categories? – Jordan Apr 07 '17 at 17:49
  • This works; however, `get_post_type_archive_link('projects')` returns with the `%project_catgory%` in the url since the slug is defined with it. – Seed Jun 19 '17 at 20:21
  • 1
    This doesn't work with pages. :/ projects/category/project-name/page/2/ results in 404. – deathlock Jul 04 '17 at 18:40
  • UPDATE: Fix for the pagination can be found here https://wordpress.stackexchange.com/a/40591/13291 – deathlock Jul 05 '17 at 08:53
  • I've followed this and am using subcategories, flushed the rewrite rules and still get a 404. Is there an htaccess edit that needs to happen as well? – Tabetha Moe Nov 04 '20 at 14:35
  • 1
    @TabethaMoe In my short experience, url rewriting is entirely managed by Wordpress, therefore you should never have to edit your htaccess. –  Dec 12 '20 at 07:06
2

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

Alletkla
  • 21
  • 2
  • 1
    Can you put a reference where you found this? It doesn't work for me. – Carl Brubaker Jul 05 '21 at 11:41
  • found that by testing on my own server. It just worked. No documentation found for that. – Alletkla Jul 07 '21 at 14:07
  • 1
    @Alletkla thanks for this. It's sad that this very basic and expected requirement for CPT / Custom Taxonomies is not clearly explained in the docs, let alone being implemented simply in Permalinks out of the box. This does NOT work for me. Any ideas on how to register the custom taxonomy along with this CPT? – Khom Nazid Apr 06 '22 at 01:44