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've got these custom post types (CPT) and meta boxes:

  • Movies (CPT)
    • Genre (Meta box)
  • Genres (CPT)

If one movie is called Die hard then I want the permalink to be: /genres/action/die-hard

This is easily fixed by setting the movie-CPT to 'rewrite' => false and use this code instead:

add_action('init', 'rb_add_rewrite_rules');
add_filter('post_type_link', 'rb_create_permalinks', 10, 3);

function rb_add_rewrite_rules() {
    global $wp_rewrite;
    $wp_rewrite->add_rewrite_tag('%movies%', '([^/]+)', 'movies=');
    $wp_rewrite->add_rewrite_tag('%genre%', '([^/]+)', 'genre=');

    $wp_rewrite->add_permastruct('movies', '/genres/%genre%/%movies%', false);
}

function rb_create_permalinks($permalink, $post, $leavename) {
    $no_data = 'no-data';
    $post_id = $post->ID;

    if($post->post_type != 'movies' || empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft')))
    return $permalink;

    $event_id = get_post_meta($post_id, 'genre', true);
    $var1 = basename(get_permalink($event_id));
    $var1 = sanitize_title($var1);

    if(!$var1)
        $var1 = $no_data;

    $permalink = str_replace('%genre%', $var1, $permalink);

    return $permalink;
}

Ok, here's the problem. Both the Die Hard permalink and the archive-genres.php (displaying all the genres) is looking good, but the single-genres.php isn't found – instead, going to /genres/action will display the index.php code.

I guess the problem is that my rewrites collide, maybe it's not possible to do this:

'movies' CPT permalink

/custom base name/custom field meta box value)/'movies' CPT post name

'genres' CPT permalink

/custom post type name (same value as the custom base name above!!)/'genres' CPT post name

How can I make WordPress use single-genres.php for /genres/action etc?

share|improve this question
    
check out the monkeyman rewrite analyzer and look at the rules that are created by your code. you may need to juggle the order in which the rules are added via add_rewrite_rule to get the more specific rule to the top. –  Milo May 14 '13 at 14:17
    
Thanks @Milo I've tried initiating the CPT before adding the rewrite rules but then the 'movies' CPT permalinks break. The problem is that they have the same base – 'genres' – hence the collision. –  Richard B May 14 '13 at 14:25
    
they have the same base, but their patterns are distinct. i'm certain this is solvable by adding the rules manually in the right order, I'll give it a shot later if I've got some time. –  Milo May 14 '13 at 14:35
    
Thanks @Milo I'd really appreciate it! I know I've had this problem before but never got my head around it, I find rewrite issues quite hard though. –  Richard B May 15 '13 at 8:06
add comment

2 Answers 2

up vote 2 down vote accepted
+50

It sounds like you're very close here and your only remaining issue is that your permalinks for genres and movies collide.

'movies' CPT permalink
/genres/%custom field meta box value%/%movie%/

'genres' CPT permalink
/genres/%genre%/

The issue here is using the add_permastruct. When it creates the rules for movies, they look something like this:

genres/([^/]+)/([^/]+)(/[0-9]+)?/?$
  => index.php?genre=$matches[1]&movies=$matches[2]&page=$matches[3]

The way request processing works in WordPress, you can't set multiple post types in the same URL -- WordPress stops after the first one. That is, they can be in the pretty URL, but not in the "ugly" one that generates the request and query. Any rewrite rules with movies= cannot contain genre=. Below is the full, exhausting overhaul of rb_add_rewrite_rules. You can pull out any rules you may not need like the attachment urls, feed urls, etc. As always when making rewrite changes, be sure to flush your permalinks by going to Settings->Permalinks and clicking "Save Changes". Also, make sure 'rewrite' => false is set on both custom post types.

function rb_add_rewrite_rules() {
    add_rewrite_rule( 'genres/[^/]+/[^/]+/attachment/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/attachment/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/trackback/?$', 'index.php?movies=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?movies=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?movies=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/page/?([0-9]{1,})/?$', 'index.php?&movies=$matches[1]&paged=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)(/[0-9]+)?/?$', 'index.php?movies=$matches[1]&page=$matches[3]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/attachment/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/attachment/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/([^/]+)/trackback/?$', 'index.php?genre=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?genre=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?genre=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/([^/]+)/page/?([0-9]{1,})/?$', 'index.php?genre=$matches[1]&paged=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/([^/]+)(/[0-9]+)?/?$', 'index.php?genre=$matches[1]&page=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'top' );
    add_rewrite_rule( 'genres/[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'top' );
}
share|improve this answer
    
Sorry for the late resonse! So, basically you're saying this can't be done? Or, can I use those rewrite rules in rb_add_rewrite_rules? I don't get it. –  Richard B May 27 '13 at 12:18
    
Replace your function with this, flush rewrites, and it will work –  Matthew Boynes May 27 '13 at 14:02
    
Also, can you let me know which part of my answer was unclear, or where I lost you? I can try to add more explanation. –  Matthew Boynes May 27 '13 at 14:44
1  
@RichardB, Here is the code I have which works wonderfully for me on a fresh WP install: gist.github.com/mboynes/079b3e43140f4a13807f (note that for the custom post type and meta box generation I'm using my plugin SuperCPT; you don't need to use it nor understand it, just recognize the 'rewrite' attribute). As far as the fatal error, I've never seen that happen, and might be a configuration issue (And if so, that's beyond the scope of this site). –  Matthew Boynes May 28 '13 at 14:03
1  
Great, glad to hear you got that sorted out! Yes, genre gets 'rewrite' => true –  Matthew Boynes May 29 '13 at 13:11
show 4 more comments

I think you're taking the wrong approach in terms of data structure. You should create a custom taxonomy called Genres, and bind your movies CPT to the Genres custom taxonomy, and then select which Genres the movie belongs to.

To address questions about your existing code. single-XXX.php template is used to display content from the CPT called XXX. So in order to display movies, you should use single-movies.php. And address content specifically for a particular post_meta, you can do the following:

if(get_post_meta($post_id, 'genres', false) == 'action') {
   //Do Stuff Here
}
share|improve this answer
    
Thanks Angela, but you're not addressing my real question here. This data structure is just an example, these aren't my real CPT names etc. The data structure is not important to my question. Also, in this case I want to display a single genre and information about that genre, therefor I'm using single-genres.php but it isn't working. –  Richard B May 14 '13 at 9:26
    
@RichardB please be specific about "isn't working". What do you expect it to do, and what did happen? –  Angela May 14 '13 at 9:29
    
Quoting myself: "Ok, here's the problem. Both the Die Hard permalink and the archive-genres.php (displaying all the genres) is looking good, but the single-genres.php isn't found – instead, going to /genres/action will display the index.php code." –  Richard B May 14 '13 at 9:39
    
@RichardB try flushing the rewrite like here gist.github.com/angelazou/5574910 –  Angela May 14 '13 at 9:58
1  
@RichardB, actually, it's different. The CPT itself register a new rewrite and sometimes if you register multiple CPTs together, it can cause the rewrite to not flush appropriately. Sorry if it didn't work for you. –  Angela May 14 '13 at 14:45
show 1 more comment

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.