2

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.

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

3
  • what is your desired single event permalink? what is your desired calendar archive page permalink? as far as querying by a date meta field is concerned, there are several examples on this site if you search around, start in the related column on this page.
    – Milo
    Feb 25, 2013 at 19:18
  • Thanks Milo, I have my calendar post type set to rewrite like so: 'rewrite' => array( 'slug' => 'event' ), --- so basically a single event permalink would appear like: mysite.com/event/my-event-title.... As far as how the calendar archive permalink would be, Im not super picky as long as its achievable - but preferably something like: mysite.com/calendar/year/month. My page that the calendar post type is queried on is titled - "Calendar" for future reference.
    – Mr.Brown
    Feb 25, 2013 at 20:32
  • you have the option to set calendar as your post type archive slug via register_post_type's has_archive argument rather than use a separate page for it.
    – Milo
    Feb 25, 2013 at 20:37

1 Answer 1

5

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 . '/';
20
  • So adding the rewrite rule seems to cause the query to not return anything anymore, or even load the correct template for that matter...this is after flushing my permalinks as well. Not sure what Im missing.
    – Mr.Brown
    Feb 26, 2013 at 6:30
  • in this example, calendar isn't a page, it's the post type archive, so the template would be archive-calendar.php, and the loop will be just the standard loop, no query within the template is necessary. also note the has_archive parameter of register_post_type. if you also have a page named calendar, it may produce strange results.
    – Milo
    Feb 26, 2013 at 6:38
  • After trouble-shooting I believe thats exactly what the problem is, their sharing the same slug which Wordpress doesnt seem to care for apparently. I tried changing the has_archive parameter to calendars instead and I was immediately able to at least get my posts to return again so i suppose Ill just keep it plural to be on the safe side.
    – Mr.Brown
    Feb 26, 2013 at 6:52
  • Is it not necessary to use add_rewrite_tag with add_rewrite_rule in this case?
    – Mr.Brown
    Feb 26, 2013 at 7:30
  • 1
    see edit above.
    – Milo
    Mar 6, 2013 at 1:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.