Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm having a difficult time trying to get my custom post types URLs to work correctly. Every solution I find has crippled another part that needs to work. What I am trying to accomplish is domain.com/post_type/post_id/post_name.

What I have that works for everything except archives is:

register_post_type('post-type',
    array(
    'rewrite' => array(
    'slug' => 'post-type/%post_id%',
    'with_front' => false,
    'pages' => true,
    'ep_mask' => 1 
    )
))

Then I have:

add_filter('post_type_link', 'custom_post_type_link', 1, 3);
function custom_post_type_link($post_link, $post = 0, $leavename = false) {

    if ($post->post_type == 'post-type')) {
        return str_replace('%post_id%', $post->ID, $post_link);
    } else {
        return $post_link;
    }
}

So I'm trying to find a way to get domain.com/post_type to work. It currently throws a 404.

I know that if I remove the filter and /%post_id% from my rewrite that the archive will work. From there I've tried adding a rewrite rule:

add_action( 'init', 'custom_rewrites_init' );
function custom_rewrites_init(){
    add_rewrite_rule(
        'post-type/([0-9]+)?$',
        'index.php?post_type=post-type&p=$matches[1]',
        'top' );
}

Doing it this way the URL wont redirect if someone types in domain.com/post_type/post_id and domain.com/post_type/post_id/post_name/2 doesn't work.

Anyone know of the best way to do this?

share|improve this question

3 Answers 3

up vote 5 down vote accepted
+50

Your code/solutions is pretty good. You use slug param in a very smart way, so WordPress automatically creates correct permastruct for this CPT.

The only thing you're missing, I guess, is has_archive param. It's default value is false and you don't set it to true. So WordPress doesn't create archive links/pages for your CPT.

has_archive (boolean or string)

(optional) Enables post type archives.

Will use $post_type as archive slug by default. Default: false Note: Will generate the proper rewrite rules if rewrite is enabled. Also use rewrite to change the slug used.

If you do this, WordPress will create rewrite rules for archive pages. There will be a tiny problem. They will contain %post_id% in them. But it's pretty easy to correct. You just have to add this code after your register_post_type call:

global $wp_rewrite;

$new_rules = array();
foreach ( $wp_rewrite->extra_rules_top as $key => $rule ) {
    echo $key;
    if (strpos($key, 'post-type/%post_id%/') === 0 ) {
        $new_rules[ str_replace('%post_id%/', '', $key) ] = $rule;
        unset( $wp_rewrite->extra_rules_top[$key] );
    }
}
$wp_rewrite->extra_rules_top = $wp_rewrite->extra_rules_top + $new_rules;

It will repair rules for archive pages created by WordPress. It's clean solution - it doesn't leave any mess behind, and it takes care of all rules for archive page (feeds, etc.)

share|improve this answer
    
Just adding has_archive => true won't solve the problem, but in addition with birgire's code it will work. –  ialocin Jun 29 '13 at 20:20
    
I played a little bit with this, and updated my answer. The problem was that WordPress created rewrite rules with %post_id% for archive pages. All you have to do is to repair them. –  Krzysiek Dróżdż Jun 29 '13 at 21:01
    
Thanks @KrzysiekDróżdż, this worked exactly how I wanted it. –  kel Jul 1 '13 at 21:01

Have you reset your permalink settings after saving the template file? Before you can use a new permalink like that you must resave your permalink settings in Settings->Permalinks.

share|improve this answer
    
Yes, I do that every time I make a change to the permalinks. –  kel Jun 27 '13 at 15:11
    
Well, to be precise you should flush rewrite rules, you don't have to resave permalink settings (you can achieve it in other/more correct ways too). –  Krzysiek Dróżdż Jun 29 '13 at 0:32

You could try to add this code snippet:

add_filter( 'rewrite_rules_array', 'custom_rewrite_rules_array' );
function custom_rewrite_rules_array( $rules ) {
        $newrules = array();
        $newrules['^post-type/page/?([0-9]{1,})/?$'] = 'index.php?post_type=post-type&paged=$matches[1]';
        $newrules['^post-type$'] = 'index.php?post_type=post-type';
       return $newrules + $rules;
}

to add support for domain.com/post-type/ and domain.com/post-type/page/2.

You just have to remember to save the permalinks.

share|improve this answer
    
Well, it's not perfect ;) 1. You leave incorrect rewrite rules created by WP. 2. You don't take care of other rewrite rules that WP creates for archive pages: feeds (not always), and so on. –  Krzysiek Dróżdż Jun 29 '13 at 21:03
    
well let me post the whole working solution that I tested the above snippet on ;-) –  birgire Jun 29 '13 at 22:09
    
Are you sure you get only these rules? You have has_archive set to true, but there are no archive rules in your output. It's a little bit strange - WordPress generates them for me (don't forget about extra_rules_top). –  Krzysiek Dróżdż Jun 29 '13 at 22:15
    
ok maybe I will test it again when I have time, will remove it untill then, thanks for the suggestions ;-) –  birgire Jun 29 '13 at 22:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.