Custom Post Type Archives by Date and Taxonomy

Lets use the classic example of a custom post type called “movies”, with its own taxonomy called “genre”.

By registering the custom post type (with a “movie” slug), the permalinks are already set up for

Read More
<domain>/movie/action/ 

…to see all movies in the action genre.

But, archives by date, like

<domain>/2010/09/

…don’t know about the custom post type.

The closest date-based archive I’m able to get working is:

<domain>/2010/?post_type=movies

…which lists all the custom post types posted during the current year (2010). For some reason, the month, taxonomy and a term cannot be added to the URL to get the expected result.

What must be done to enable url’s like these…

<domain>/2010/09/movie/action/

…to work as expected, thus to list all action movies posted during September 2010?

Related posts

Leave a Reply

3 comments

  1. Yes, there isn’t currently built-in support for CPT archives, but that doesn’t mean you can’t extend WP to provide it. I just did this myself the other day…

    This won’t create the date-based archives you’re looking for, but it will give you virtual archive behavior for custom post types. Adding the date should just be a matter of tweaking the rewrite rules (actually, date-based permalinks might just work as-is)…

    EXAMPLE: you have a custom type of “movies” and single movie post called “gone with the wind”. This code will give you a URL structure of website.com/movies/gone-with-the-wind. Also, going to website.com/movies will list just the movies (just like a category archive, though it will not call the archive.php template for output, but will format the output just like the standard index.php loop template).

    function register_post_type_archives( $post_type, $base_path = '' ) {
        global $wp_rewrite;
        if ( !$base_path ) {
            $base_path = $post_type;
        }
        $rules = $wp_rewrite->generate_rewrite_rules($base_path);
        $rules[$base_path.'/?$'] = 'index.php?paged=1';
        foreach ( $rules as $regex=>$redirect ) {
            if ( strpos($redirect, 'attachment=') == FALSE ) {
                $redirect .= '&post_type='.$post_type;
                if (  0 < preg_match_all('@$([0-9])@', $redirect, $matches) ) {
                    for ( $i=0 ; $i < count($matches[0]) ; $i++ ) {
                        $redirect = str_replace($matches[0][$i], '$matches['.$matches[1][$i].']', $redirect);
                    }
                }
            }
            add_rewrite_rule($regex, $redirect, 'top');
        }
    }
    

    call this function right after having generated your custom post type:

    register_post_type('movies', $args);
    register_post_type_archives('movies');
    

    Then, if you would like to be able to use custom templates to control the output of these quasi-archive listings, you can use this:

    add_action('template_redirect', 'post_type_templates');
    function post_type_templates() {
        $post_type = get_query_var('post_type');
        if (!empty($post_type)) {
            locate_template(array("{$post_type}.php","index.php"), true);
            die;
        }
    }
    

    Now you can create a “movies.php” template in your theme and customize the loop output to your liking..

    UPDATE: having the archive functionality for custom types is great, but I realized I needed a way to access them. You can obviously hard-code buttons somewhere that point to the slugs, but I made a function to generate a wp3.0 navbar with all my custom types in it. Right now it spawns a new navbar and makes it the primary, but you could change it to be the secondary, or to just add the items to an existing navbar. Note: the nav links will only work if you’re using the rewrite rules from above.

    function register_typenav() {
        $mainnav = wp_get_nav_menu_object('Types Nav');
        if (!$mainnav) {
            $menu_id = wp_create_nav_menu( 'Types Nav' );
            // vav item for each post type
            $types = get_post_types( array( 'exclude_from_search' => false ), 'objects' );
            foreach ($types as $type) {
                if (!$type->_builtin) {
                    wp_update_nav_menu_item( $menu_id, 0, array(
                        'menu-item-type' => 'custom',
                        'menu-item-title' => $type->labels->name,
                        'menu-item-url' => get_bloginfo('url') . '/' . $type->rewrite['slug'] . '/',
                        'menu-item-status' => 'publish'
                        )
                    );
                }
            }
        if ($mainnav && !has_nav_menu( 'primary-menu' ) ) {
            $theme = get_current_theme();
            $mods = get_option("mods_$theme");
            $key = key($mods['nav_menu_locations']);
            $mods['nav_menu_locations'][$key] = $mainnav->term_id;
            update_option("mods_$theme", $mods);
        }
    }
    add_action('init', 'register_typenav');