Archives for custom post type based on a “date” meta value

I did something similar for a client a while back, I’ll give you some of the code here as-is that you can possibly adapt to your needs. I’ll warn you, it’s quite a bit to parse through!

First, I set up some custom rewrite rules to get the year/month URL structure and some query vars to pass the year and month to my template. In this example I have a page set up with the slug event-calendar that I use as my events list / archive page. This code would go in functions.php:

<?php
add_action( 'init', 'wpse_rewrites_init' );
function wpse_rewrites_init(){
    // after adding, visit settings > permalinks to flush rewrite rules!
    add_rewrite_rule(
        'event-calendar/([0-9]+)/([0-9]+)/?$',
        'index.php?pagename=event-calendar&wpse_year=$matches[1]&wpse_month=$matches[2]',
        'top' );
    add_rewrite_rule(
        'event-calendar/([0-9]+)/?$',
        'index.php?pagename=event-calendar&wpse_year=$matches[1]',
        'top' );
}

add_filter( 'query_vars', 'wpse_query_vars' );
function wpse_query_vars( $query_vars ){
    $query_vars[] = 'wpse_year';
    $query_vars[] = 'wpse_month';
    return $query_vars;
}

This is the code in my page-event-calendar.php template to query for events based on the year and month (if it’s set), otherwise I show just upcoming events:

<?php
// default args, upcoming events
$args = array(
    'posts_per_page' => -1,
    'meta_key' => 'event_date',
    'meta_value' => date('Y-m-d'),
    'meta_compare' => '>=',
    'orderby' => 'meta_value',
    'order' => 'ASC'            
);

// get the year and month query vars
$wpse_year = get_query_var('wpse_year');
$wpse_month = get_query_var('wpse_month');

// if a month was set we query for the requested month/year (we assume year is set if month is)
if($wpse_month):
    $estart = $wpse_year.'-'.$wpse_month.'-01';
    $eend = $wpse_year.'-'.$wpse_month.'-31';
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            array(
                'key' => 'event_date',
                'value' => array( $estart, $eend ),
                'compare' => 'BETWEEN',
                'type' => 'date',
            )
        ),
        'orderby' => 'meta_value',
        'order' => 'ASC'
    );
endif;

// if just a year is set, we query for the requested year
if($wpse_year&&!$wpse_month):
    $estart = $wpse_year.'-01-01';
    $eend = $wpse_year.'-12-31';
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            array(
                'key' => 'event_date',
                'value' => array( $estart, $eend ),
                'compare' => 'BETWEEN',
                'type' => 'date',
            )
        ),
        'orderby' => 'meta_value',
        'order' => 'ASC'
    );
endif;

// query for our events
$events = new WP_Query($args);
while($events->have_posts()) : $events->the_post();
    // do your normal loop stuff here
endwhile;

To output an archive list of years / months for my past events, I do some custom SQL to get all unique meta values for the event_date key. It’s quicker than doing a join with the actual posts, but the downside is you’ll get meta values for possibly unpublished posts. This was ok for my client’s needs, but possibly not yours.

$query = "SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'event_date' AND DATE(meta_value) < DATE(NOW()) ORDER BY meta_value DESC";
$all_unique_dates = $wpdb->get_results($query);

You could remove the AND DATE(meta_value) < DATE(NOW() condition if you want future events in the list as well.

ok, that’s it! Hope you find some of this useful.

Leave a Comment