• Custom Post Type Permalinks – Part 2
    by ShibaShake on
    [   See Full Article   ]
    105 Comments
    1. Ian

      Thanks so much for this in depth post. Really saved me on a bad permalink custom post type I had set up. I updated the permalink structure and they all broke. They are fixed now!

      3:44 pm on May 8th, 2012 Reply
    2. Gam

      There was a question about showing cats and sub-cats in the url, like you can with regular wordpress categories. Of note here is when you register the taxonomy, there is a setting in the rewrite rules to do this.

      Under rewrite, the argument pair ‘hierarchical’ => true, turns on allowing /topic/sub-topic/super-sub-topic/, based on your structure, in the example called topic.


      register_taxonomy('topic', 'gam_qa',
      array(
      'labels' => array(
      'name' => _x( 'Topics', 'taxonomy general name' ),
      'singular_name' => _x( 'Topic', 'taxonomy singular name' ),
      'search_items' => __( 'Search Topics' ),
      'all_items' => __( 'All Topics' ),
      'parent_item' => __( 'Parent Topic' ),
      'parent_item_colon' => __( 'Parent Topic:' ),
      'edit_item' => __( 'Edit Topic' ),
      'update_item' => __( 'Update Topic' ),
      'add_new_item' => __( 'Add New Topic' ),
      'new_item_name' => __( 'New Topic Name' ),
      'menu_name' => __( 'Topics' ),
      ),
      'public' => true,
      'show_ui' => true,
      'hierarchical' => true,
      'query_var' => true,
      'rewrite' => array( 'slug' => 'q-and-a/topic', 'with_front' => true, 'hierarchical' => true ),
      'capabilities' => array(
      'manage_terms' => 'manage_qa_terms',
      'edit_terms' => 'edit_qa_terms',
      'delete_terms' => 'delete_qa_terms',
      'assign_terms' => 'assign_qa_terms'
      )
      )
      );

      Then the function you call from. This is a stripped down version, using only the %topic% tag, which will be the name of your custom taxonomy.

      The code looks for a parent to the topic, and as long as it finds one, adds it to the url.


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

      $post = get_post($post_id);
      $rewrite_code = array(
      '%topic%'
      );

      if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {

      if ( strpos($permalink, '%topic%') !== false ) {
      $terms = wp_get_object_terms($post->ID, 'topic');
      if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) {
      $topics[] = $terms[0]->slug;

      $parent = $terms[0]->parent;
      while($parent) {
      $term = get_term($parent, 'topic');
      $parent = $term->parent;
      $topics[] = $term->slug;
      }
      $topics = array_reverse($topics);

      $taxonomy_slug = join('/', $topics);
      }else{
      $taxonomy_slug = 'no-topic';
      }
      }else{
      $taxonomy_slug = '';
      }

      $rewrite_replace =
      array(
      $taxonomy_slug
      );
      $permalink = str_replace($rewrite_code, $rewrite_replace, $permalink);
      } else { // if they're not using the fancy permalink option
      }
      return $permalink;
      }

      6:06 pm on April 25th, 2012 Reply
    3. You just saved my sanity. I’ve been fighting with these dang ole permalinks/pagination issues for the last week straight.

      Great stuff, keep it up and thanks!

      2:02 pm on April 18th, 2012 Reply
    4. Amy

      Very basic error, not sure if I’m being silly. I’ve put the ‘// Add filter to plugin init function’ at the top of functions and then am using a custom post type called features, followed all your steps above.

      I want my structure to be

      features/%year%/%month/%day%/%postname%

      but I’m just getting a 404 error?

      add_action('init', 'feature_register');

      function feature_register() {

      $labels = array(
      'name' => _x('Feature', 'post type general name'),
      'singular_name' => _x('Feature', 'post type singular name'),
      'add_new' => _x('Add New', 'Feature item'),
      'add_new_item' => __('Add New Feature'),
      'edit_item' => __('Edit Feature Item'),
      'new_item' => __('New Feature Item'),
      'view_item' => __('View Feature Item'),
      'search_items' => __('Search Feature'),
      'not_found' => __('Nothing found'),
      'not_found_in_trash' => __('Nothing found in Trash'),
      'parent_item_colon' => ''
      );

      $args = array(
      'labels' => $labels,
      'public' => true,
      'publicly_queryable' => true,
      'show_ui' => true,
      'query_var' => true,
      'rewrite' => false,
      'capability_type' => 'post',
      'hierarchical' => false,
      'has_archive' => true,
      'menu_position' => null,
      'supports' => array('title','editor','thumbnail', 'comments','trackbacks', 'author'),
      'taxonomies' => array('post_tag'),
      );

      register_post_type( 'feature' , $args );
      }

      // add to our plugin init function
      global $wp_rewrite;
      $feature_structure = '/features/%year%/%monthnum%/%day%/%postname%';
      $wp_rewrite->add_rewrite_tag("%feature%", '([^/]+)', "feature=");
      $wp_rewrite->add_permastruct('feature', $feature_structure, false);

      Any help appreciated, thanks

      6:35 am on March 27th, 2012 Reply
      • Try
        ‘/features/%year%/%monthnum%/%day%/%feature%’

        8:05 pm on March 27th, 2012 Reply
    5. Xevo

      Hi, nice article, but is it possible to use a custom post meta in the permalink?

      9:30 am on January 24th, 2012 Reply
    6. Amanda

      Thanks for the info! It worked great to change the permalinks on my custom post type, but I am getting a 404 when I try to view the post? Is there something I’m missing? I’ve tried saving the Permalinks Settings Page, but that hasn’t done anything. Thanks in advance!

      1:59 pm on December 21st, 2011 Reply
      • Amanda

        Sorry, I wrote too soon! I saw you already answered it in the previous comments and that worked for me too. Thanks again for the great info!

        2:03 pm on December 21st, 2011 Reply
    7. Rafal Jaskolski

      Hi :)

      great thanks for this tutorial. It works for me, but….

      when I set rewrite as “%category%” im my post custom type there is conflict with pemalink for standard post.

      How can I solve this?

      5:48 am on November 2nd, 2011 Reply
    8. Hello there, thanks for your job, I have set a permalink structure for my custom type ‘spartiti’ in this way:
      /%year%/%monthnum%/%day%/%postname%/
      but I would like to have my archive for this custom post type in this way:

      mywebsite/spartiti

      Is this possible?
      Thank you

      6:00 am on October 9th, 2011 Reply
    9. Works great, thanks a billion!!! I modified it slightly for a custom post that I wanted to display like /books/%post_id%/%postname% so I was able to remove much of the code, and it looks like this:


      function BookPermalink( $permalink, $post_id, $leavename )
      {
      $post = get_post( $post_id );

      // Don't rewrite unless it's published
      if ( '' != $permalink && !in_array( $post->post_status, array('draft', 'pending', 'auto-draft') ) )
      {

      // These are the things we'll look for
      $rewrite_old = array(
      '%post_id%',
      $leavename? '' : '%pagename%',
      $leavename? '' : '%postname%'
      );

      // And here we decide what to replace them with
      $rewrite_new = array(
      $post->ID,
      $post->post_name,
      $post->post_name
      );

      // Now do the replacing
      $permalink = str_replace($rewrite_old, $rewrite_new, $permalink);

      }

      return $permalink;
      }

      6:09 pm on September 29th, 2011 Reply
    10. Steven

      Hi,

      I am having problems with creating this for two CPT,
      $wp_rewrite->add_permastruct(‘event’, $event_structure, false);
      $wp_rewrite->add_permastruct(‘place’, $place_structure, false);

      They seem to cancel each other out, if i comment one out the other will work but not both, do u have any advice?

      Steven

      8:06 am on September 25th, 2011 Reply
      • What is $event_structure and $place_structure?

        3:37 pm on September 28th, 2011 Reply
    11. Hey Shibashake, thanks for the awesome post on a topic I can’t find much info on anywhere else. If you’re still checking this post, could you help? I’m having trouble implementing this, as so:

      add_action('init', 'videos_rewrite');

      function videos_rewrite() {
      global $wp_rewrite;
      $wp_rewrite->add_permastruct('videos', '/videos/%year%/%monthnum%/%postname%', false);

      }

      Along with your post_type_link hook. The permalinks change correctly, but all head to 404 pages. Am I doing something wrong? I know the structure is to be slightly different from your own.

      Thanks in advance.

      6:21 am on September 17th, 2011 Reply
      • Try using %videos% (or whatever rewrite tag you are using for your custom post type) instead of %postname%.

        7:58 am on September 17th, 2011 Reply
        • Thanks for getting back to me. I’ve attempted:

          function videos_rewrite() {
          global $wp_rewrite;

          $wp_rewrite->add_rewrite_tag("%videos%", '([^/]+)', "p=");
          $wp_rewrite->add_permastruct('videos', '/videos/%year%/%monthnum%/%videos%', false);

          }

          (note the p= rather than videos= as my non-fancy permalinks are http://domain.com/?post_type=videos&p=1234 rather than how you describe. This may be where I’m going wrong?)

          Now that I’ve refreshed permalinks the page redirects to monthly archives, so http://domain.com/videos/2011/09/video-title is actually displaying what is located at http://domain.com/2011/09. Quite frustrating! Any thoughts on my problems, greatly appreciated!

          4:08 am on September 18th, 2011 Reply
          • Yes p= expects the post id whereas your %videos% rewrite tag is passing it the post name (e.g. video-title) instead. I would suggest using a regular query argument (e.g. videos) rather than ‘p’ since p is the wordpress standard for post id.

            11:01 am on September 23rd, 2011 Reply
    12. Léon

      Hi,

      Thanks for this great example. However I do have a problem.
      I have the custom post type “Game”. I use the code exactly as above but when this is activated my normal posts and pages won’t work anymore. It says “Page cannot be found”. Any ideas how this is possible?
      I have resaved permalink structure.
      The permalink for posts is /%year%/%postname%

      regards,
      Léon

      2:01 am on September 14th, 2011 Reply
      • Hello Léon,
        What is the permalink structure of your custom post type?

        It sounds like a permalink conflict issue.
        http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2#conflict

        2:10 pm on September 15th, 2011 Reply
        • Léon

          Hi,

          Sorry for late reply.
          Structure is: /%ean%/%game%


          $wp_rewrite->add_rewrite_tag("%game%", '([^/]+)', "game=");
          $wp_rewrite->add_permastruct('game', '/%ean%/%game%', false);
          add_filter('post_type_link', 'game_permalink', 10, 3);

          %ean% is replaced by a custom taxonomy value. Which works fine.
          In the gallery_permalink() function I added:

          if ( strpos($permalink, '%ean%') !== false ) {
          $terms = wp_get_post_terms( $post_id->ID, 'ean' );
          $ean = ( !empty( $terms[0]->name ) && is_numeric( $terms[0]->name ) ) ? $terms[0]->name : '1';
          }

          3:16 am on September 19th, 2011 Reply
          • Yes this is a premalink conflict issue. Try adding a unique keyword to your permalink structure, e.g.

            /my-unique-keyword/%ean%/%game%

            11:04 am on September 23rd, 2011 Reply
    13. Hi Shibashake,

      Thanks for your great tutorials! Always super in-depth :)

      I was able to follow your tutorial and get this working on a website I’m building. One question I do have though, is how can I make archives work for the cpt? i.e. I would like to display a list of all posts made in 2010 when visiting:
      http://shibashake.com/wordpress-theme/galleries/2010/

      Likewise, http://shibashake.com/wordpress-theme/galleries/2010/06/ would display all posts from June of 2010.

      Thanks!

      1:16 pm on September 2nd, 2011 Reply
      • Hello James,
        One way is to hook into the request filter and then manually insert in the custom post type into the query args. If the custom post type is not specified, then it defaults to ‘post’.

        This article has an example of hooking into the request filter -
        http://shibashake.com/wordpress-theme/mastering-the-wordpress-loop

        I do some of this on my blog-art site which shows archives, recent posts, etc all on my gallery custom post type.

        10:39 am on September 7th, 2011 Reply
    14. Jurre

      Good article, but I keep wondering what’s the deal with all digital women on this site hmm?

      1:19 pm on August 14th, 2011 Reply

    Leave a Reply

    Your email address will not be published.

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

    search button search button
    rss