How to filter custom post type archive by meta value

I have a problem where I’m trying to build a calendar plugin for a client. I’m having trouble getting a date based archive for the custom post type I’m using. I’m not too well versed in how the wordpress system for handling permalinks is set up on a low level, and I’ve hit a wall.

Background:

The plugin registers a custom post type, event. Each event has a meta value field called kultkal_date, holds a date in the form of 2011-09-28. (Events are limited in the sense that each event’s date is unique as well, by validation on the save_post action).

I’ve defined a custom permalink for the post type as /kalendarie/%kultkal_year%/%kultkal_month%/%kultkal_day%, so the finished URL might look like /kalendarie/2011/09/28/my-slug. I’ve added rewrite tags for the custom permalink using the init action like so:

add_rewrite_tag('%kultkal_year%','([0-9]{4})');
add_rewrite_tag('%kultkal_month%','([0-9]{2})');
add_rewrite_tag('%kultkal_day%','([0-9]{2})');

I have a function to fix the permalink that runs on the post_type_link filter:

function filter_post_type_link($link, $post){
    if ($post->post_type != 'event')
        return $link;

    if ($date = get_post_meta($post->ID, 'kultkal_date', true)) {
        $dates = explode('-', $date);
        $year = $dates[0];
        $month = $dates[1];
        $day = $dates[2];
    } else {
        return $link;
    }
    $link = str_replace('%kultkal_year%', $year, $link);
    $link = str_replace('%kultkal_month%', $month, $link);
    $link = str_replace('%kultkal_day%', $day, $link);
    return $link;
}

Viewing individual posts works. So does the archive page (located at the /kalendarium/ URL: has_archive is set to kalendarium when I register the post type).

The trouble starts when I want to filter the posts by year – for instance, /kalendarium/2011/ or /kalendarium/2011/09/28/ etc. I’ve found an answer on how to filter based on meta value for date on built-in posts here: Archive Listings Filtered by Date Values in a Custom Field/Post Meta? – but it doesn’t seem to work on custom post types. As soon as I add /year/month/day-parameters to test it, I get the archives for the built-in post type, not even a 404. (So far, I’ve only tried the first version in the answer above, with all the parameters (year/month/date), not the one with all the combinations + hook for posts_where etc.

Here’s what the (broken) function I run on pre_get_posts looks like:

function event_pre_get_posts($query) {
    global $wp_the_query;
    if ($query === $wp_the_query && $query->is_archive()) {
        $qv = &$query->query_vars;
        if (isset($qv['kultkal_year']) && isset($qv['kultkal_month']) && isset($qv['kultkal_day'])) {
            $date = mktime(0,0,0,$qv['kultkal_month'],$qv['kultkal_day'],$qv['kultkal_year']);
            $qv['meta_key'] = 'kultkal_date';
            $qv['meta_value'] = date("Y-m-d", $date);
        }
    }
}

Any ideas on how to achieve the solution I’m looking for?

1 Answer
1

Like Bainternet said in comments, you’ll need to mess with add_query_var as well as add_rewrite_rule.

We usually do something similar to:

global $wp;
$wp->add_query_var( 'some_var' );
add_rewrite_rule( 'some/path/([^/]+)/?$', 'index.php?some_var=$matches[1]' );

See this WordPress Trac ticket, as well as documentation on add_query_var and add_rewrite_rule

Leave a Comment