Rewrite custom post type url’s adding meta box values

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

Read More

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?

Related posts

2 comments

  1. 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' );
    }
    
  2. 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
    }
    

Comments are closed.