How to sort CPT by custom meta value (date), and return posts month by month

For future reference:
I am using the great Meta Box Plugin to help expedite meta box creation for my custom post types.

My goal:
To build a highly customizable events section closely resembling a traditional calendar like get_calendar creates, but for a specific custom post type.

Read More

The Problem:
— By relying solely on using WordPresses built in time parameters, I am able to return my posts in an ASC order similar to a traditional calendar but I am limited to only showing posts up to the current day since there is no way to query and return scheduled posts – this is an issue obviously since I need to display ALL event posts for the current month, and onward…
— Additionally, I also need to be able to page to “previous” and “upcoming” months similar to get_calendar.

My Solution Thus Far:
— Due to the time parameter restrictions, I’ve opted to return my posts via WP_Query‘s meta_key parameter instead and assigned it to my events “startdate” field, and then orderby – meta_value_num

This seems to replicate the same functionality Im after for the most part but now I’m posed with a serious issue of:

“how do i treat this meta value just like wordpresses native time parameters so that I can store this data appropriately for each month and have the option to page through the months similar to the get_calendar archive?”

Hopefully I was definitive enough in my explanation to help anyone who reads this make sense of it all. If not, please let me know and I will happily attempt to further clarify whatever it is…and thank you for the help!


Pasted below is my current “Calendar/Events” template as well as a screenshot of how it will most likely look on the front end.

<?php
/*
Template Name: Calendar
*/
?>

<?php get_header(); ?>

<!-- featured_images.php -->
<?php include ('featured_images.php'); ?>

    <div id="full_col">

        <h2 class="feed_title_full"><?php wp_title('', true); ?></h2>
        <div id="calendar_nav">
            <h4 id="current_month"><?php the_time( 'F Y' ); ?></h4>
        </div>

        <ul id="calendar">

            <?php $current_year = date('Y'); // Get current YEAR ?> 
            <?php $current_month = date('m'); // Get current MONTH ?>
            <?php $calandar_posts = new WP_Query(
                array(
                    'post_type' => 'calendar', 
                    'year' => $current_year,
                    'monthnum' => $current_month, // Show ALL posts for current Month 
                    'meta_key' => 'epr_startdate',
                    'orderby' => 'meta_value_num',
                    'order' => 'ASC', 
                    'posts_per_page' => -1, 
                    'paged' => get_query_var('paged') ? get_query_var('paged') : 1,
                )); 
            ?>
            <?php if($calandar_posts->have_posts()) : while($calandar_posts->have_posts()) : $calandar_posts->the_post(); ?>

                <li class="calendar_entry">
                    <a href="<?php the_permalink(); ?>" class="calendar_link" title="<?php the_title_attribute(); ?>"></a>
                    <div class="entry_date">
                        <?php echo get_post_meta($post->ID, 'epr_startdate', TRUE); ?>
                    </div>
                    <div class="shadow_overlay"></div>
                    <?php the_post_thumbnail('calendar-teaser', array('class' => 'calendar-teaser-img', 'alt' => 'View Event')); ?>
                </li>

            <?php endwhile; else: ?>
                <h2>No Events for the month of <?php the_time( 'F' ); ?></h2>
            <?php endif; ?>
            <?php wp_reset_query(); ?>

        </ul>

    </div>

<?php get_footer(); ?>

enter image description here

Related posts

Leave a Reply

1 comment

  1. This isn’t complete copy/paste code, but hopefully it’s understandable enough to get you started.

    First step is to register your post type and add a rewrite rule to handle years/months. This will give you single events at event/post-name/, your post type archive at calendar, and handle incoming requests for calendar/yyyy/mm/. Make sure to visit your Settings > Permalinks page after this is added to flush the rewrite rules.

    function wpa88173_calendar_post_type() {
    
        // just the important bits shown here
        $args = array(
            'rewrite' => array( 'slug' => 'event' )
            'has_archive' => 'calendar',
        );
    
        register_post_type( 'calendar', $args );
    
        add_rewrite_rule(
            '^calendar/([0-9]{4})/([0-9]{2})/?',
            'index.php?post_type=calendar&calendar_year=$matches[1]&calendar_month=$matches[2]',
            'top'
        );
    
    }
    add_action( 'init', 'wpa88173_calendar_post_type' );
    

    Next step is to add the calendar_year and calendar_month query vars so WordPress adds them to the array of query vars when the incoming requests are parsed.

    function wpa88173_calendar_query_vars( $query_vars ) {
        $query_vars[] = 'calendar_year';
        $query_vars[] = 'calendar_month';
        return $query_vars;
    }
    add_filter('query_vars', 'wpa88173_calendar_query_vars' );
    

    The next step is to add an action to pre_get_posts, which checks if it’s the calendar post type archive, fetches the year/month or sets it to the current year/month, then modifies the query with meta_query parameters to load the requested year/month. See WP_Query for more info on meta queries. This assumes a date format of yyyymmdd.

    function wpa88173_calendar_query( $query ) {
        // is it a post type archive?
        if( ! $query->is_post_type_archive( 'calendar' ) )
            return;
    
        // is it the main query and not an admin page?      
        if( $query->is_main_query()
            && ! is_admin() ) {
    
            // check if year/month was set via the URI, or set it to current year/month
            ( ! empty( $query->query_vars['calendar_year'] ) ) ? $query_year = $query->query_vars['calendar_year'] : $query_year = date('Y');
            ( ! empty( $query->query_vars['calendar_month'] ) ) ? $query_month = $query->query_vars['calendar_month'] : $query_month = date('m');
    
            // meta_query parameters for events between start and end dates
            $date_start = $query_year . $query_month . '01';
            $date_end = $query_year . $query_month . '31';
            $meta_query = array(
                array(
                    'key' => 'event_date',
                    'value' => array( $date_start, $date_end ),
                    'compare' => 'BETWEEN',
                    'type' => 'NUMERIC'
                )
            );
    
            // modify the query
            $query->set( 'meta_key', 'event_date' );
            $query->set( 'orderby', 'meta_value_num' );
            $query->set( 'order', 'ASC' );
            $query->set( 'meta_query', $meta_query );
    
        }
    
    }
    add_action( 'pre_get_posts', 'wpa88173_calendar_query' );
    

    The last step will be for you to build the calendar in your template and create the next/previous links to page through months. You can get the queried year/month in the template via get_query_var.

    EDIT – Here’s an example of building the links with plain ol’ math

    ( '' == get_query_var( 'calendar_month' ) ) ? $this_month = date( 'n' ) : $this_month = ltrim( get_query_var( 'calendar_month' ), '0' );
    ( '' == get_query_var( 'calendar_year' ) ) ? $this_year = date( 'Y' ) : $this_year = get_query_var( 'calendar_year' );
    
    if( 1 == $this_month ):
        $next_month = 2;
        $prev_month = 12;
        $next_year = $this_year;
        $prev_year = $this_year - 1;
    elseif( 12 == $this_month ):
        $next_month = 1;
        $prev_month = 11;
        $next_year = $this_year + 1;
        $prev_year = $this_year;
    else:
        $next_month = $this_month + 1;
        $prev_month = $this_month - 1;
        $next_year = $this_year;
        $prev_year = $this_year;
    endif;
    
    $next_month = str_pad( $next_month , 2, '0', STR_PAD_LEFT );
    $prev_month = str_pad( $prev_month , 2, '0', STR_PAD_LEFT );
    
    echo 'next month: /calendar/' . $next_year . '/' . $next_month . '/';
    echo 'previous month: /calendar/' . $prev_year . '/' . $prev_month . '/';