Date based URLs for custom posts and pagination

I have created a custom permalink structure for a custom post type (events) using the code below:

$GLOBALS["wp"]->add_query_var("event_year");
$GLOBALS["wp"]->add_query_var("event_monthnum");
$GLOBALS["wp"]->add_query_var("event_day");

$GLOBALS["wp_rewrite"]->add_rewrite_tag("%event%", "([^/]+)", "event=");
$GLOBALS["wp_rewrite"]->add_rewrite_tag("%event_year%", "([0-9]{4})", "event_year=");
$GLOBALS["wp_rewrite"]->add_rewrite_tag("%event_monthnum%", "([0-9]{1,2})", "event_monthnum=");
$GLOBALS["wp_rewrite"]->add_rewrite_tag("%event_day%", "([0-9]{1,2})", "event_day=");

$GLOBALS["wp_rewrite"]->add_permastruct("event", "/event/%event_year%/%event_monthnum%/%event_day%/%event%", false);

For the most part this works just as expected. A problem however arises when I try to use pagination on a URL with a year, month and date. In which case it is assumed that “page” is the value for %event%.

I used the Monkeyman Rewrite Analyzer plugin where I noticed the required pattern (event/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$) is listed, but only after the pattern that tries to use the page argument as event name (event/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/([^/]+)(/[0-9]+)?/?$).

I also noticed that calling $GLOBALS["wp_rewrite"]->rewrite_rules() after the rewrite definitions will make pagination work but breaks URLs with event names (somehow event_year, event_monthnum, event_day and event translate to $1, $2, $3 and $4 in that case).

Calling $GLOBALS["wp_rewrite"]->flush_rules() did make everything work as I required but that wouldn’t be a good solution.

Is there some way I could flip the patterns so that the pattern that looks for a page comes before the pattern that looks for a post name?

1 Answer
1

Calling $GLOBALS[“wp_rewrite”]->flush_rules() did make everything work as I required but that wouldn’t be a good solution.

Actually, you have to flush the rewrite rules after modifying them (i.e. also when registering custom post types).

Also, you should never rely on these global variables. WordPress provides specific functions for nearly everything. For example, use flush_rewrite_rules() instead of $GLOBALS["wp_rewrite"]->flush_rules(). It’s more future-proof and maintainable.

It’s important you call these functions at the appropriate action hooks, to ensure all needed classes and functions are loaded. When, wrapped in a plugin, it could look like this (untested!):

    function events_87669_query_vars( $qvars ) {
    $qvars[] = 'event_year';
    $qvars[] = 'event_monthnum';
    $qvars[] = 'event_day';
    return $qvars;
}
add_filter('query_vars', 'events_87669_query_vars' );

function events_87669_rewrite_rules() {
    add_rewrite_tag("%event%", "([^/]+)", "event=");
    add_rewrite_tag("%event_year%", "([0-9]{4})", "event_year=");
    add_rewrite_tag("%event_monthnum%", "([0-9]{1,2})", "event_monthnum=");
    add_rewrite_tag("%event_day%", "([0-9]{1,2})", "event_day=");

    add_permastruct("event", "/event/%event_year%/%event_monthnum%/%event_day%/%event%", false);
}

add_action( 'init', 'events_87669_rewrite_rules' );

function events_87669_activation() {
    // Ensure our rewrite rules exist on plugin activation
    events_87669_rewrite_rules();

    flush_rewrite_rules();
}

function events_87669_activation() {
    flush_rewrite_rules();
}

register_activation_hook( __FILE__, 'events_87669_activation' ) );
register_deactivation_hook( __FILE__, 'events_87669_deactivation' ) );

This code does basically the same as yours, except it properly flushes the rewrite rules on plugin activation/deactivation. Also, we ensure the newly added rewrite rules exist on activation. And as you can see, there’s a special filter for adding custom query vars.

A good example for custom queries can be found at the WordPress Codex: http://codex.wordpress.org/Custom_Queries
If you want to learn more about hooks and filters: http://codex.wordpress.org/Plugin_API

If anybody knows a better solution, let me know 🙂

Leave a Comment