Sorting Events by Date Using
WordPress Custom Post Types

June 7, 2011

An Events Custom Post Type for WordPress MultiSite

WordPress

Recently at DesignHammer I've been working on a WordPress MultiSite project that required us to build a fairly feature-rich theme from scratch. As such, it was important that any custom features work fluidly within the MultiSite framework. We needed some custom widgets, custom post types, and a few other bells and whistles. One thing we were trying to achieve was an Events listing solution that was fundamentally simple. Date, times, location and a description.

Many of the events plugins out there seem to store their data in a database table specifically created for the plugin, something that won't play nice with MultiSite. The ideal solution would be a list of events that stores its data in each network site's unique database table structure. Therefore, we chose to set this up as a custom post type with a date picker attached to a custom meta box. We would store the date in a custom field. Simple enough. One thing that had us racking our brains for a while was how to sort the dates in a useful way.

Listing the posts sorted by the custom field itself was the first step, but this wasn't very useful. When querying by post type and listing out the events, by default we would get back either the first post in the list, or the last post. However, it would be infinitely more useful to check for the current date, and show the post that was most relevant to the current date. In other words, show the most current event.

In the hopes of saving someone else some anguish, here's how we did it.

Prerequisites:

Building the Meta Box

In order to support the date picker, we have to add a meta box type to the "framework" we're using to build the meta boxes. Together with the JS for the date picker, here's the code that we're using to display the "date" meta box:

1
2
3
4
5
    case 'date':
        echo '<input type="text" name="', $field['id'], '" class="event_date" id="', $field['id'], '" value="', $meta ? date('m/d/y', $meta) : $field['std'], '" size="30" style="width:100px" />',
        '<br />', $field['desc'];
        echo '<script type="text/javascript">jQuery(document).ready(function(){jQuery(".event_date").simpleDatepicker();});</script>';
    break;

This is added to the show() function, which allows the meta boxes to be displayed on the post edit page. Note that we're converting the information that's stored as the date to the format 'm/d/y'. That's because we're storing the date field as a unix timestamp. I'll explain why in the next step:

Storing the Event Date

At first, we were storing the event date as produced by the date picker, your usual 01/01/11 date format. This is useful to the user as a recognizable date format, but not very useful for database sorting. So while preserving the human-readable date in our custom field, we convert the date to a unix timestamp while saving the meta box:

1
2
3
4
5
6
7
8
9
10
11
12
13
    foreach ($this->_meta_box['fields'] as $field) {
        $old = get_post_meta($post_id, $field['id'], true);
        $new = (isset($_POST[$field['id']])) ? $_POST[$field['id']] : '';
 
    if ($new && $new != $old) {
        if ($field['type'] == 'date') {
            $new = strtotime($new);
        }
        update_post_meta($post_id, $field['id'], $new);
    } else if ('' == $new && $old) {
        delete_post_meta($post_id, $field['id'], $old);
    }
 

Here (line 6) we insert an if statement that says basically "if the field type is equal to 'date', convert it using strtotime() to a unix timestamp." Now we're ready to start our queries and sorting!

Displaying our Events

In order to give the user some flexibility as to where the events are displayed, we've built our own widget to serve as the platform for listing the events, using the WordPress widget API documentation.

We'll be performing the query inside of the widget function. This query can of course be performed anywhere in your theme template at the beginning of the loop.

The Wp_Query class documentation helped us to put together our query.

In this query, we want to get all the posts from our custom post type, in this case "Events". We want to show exactly 1. Then we want to show all the posts that are greater than or equal to the current date, and sort them in ascending order. Here are the arguments for that query:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    global $post;     
    $args = array(
        'post_type' => 'events',
        'posts_per_page' => 1,
        'meta_query' => array(
                array(
                        'key' => 'mbs_thedate',
                        'value' => time(),
                        'compare' => '>=',
                ),
        ),
        'orderby' => 'key',
        'order' => 'ASC',
     );

The meta_query array (line 5) allows us to do two things: 1 - take our custom field data (currently stored as the unix timestamp), and compare it to a value, in this case we've specified that value as time(), which gives us the unix timestamp for the current date and time. Now we can continue with the job of creating our WP loop to show our post data:

1
2
3
4
5
6
7
8
9
10
11
12
           
    $query = new WP_Query( $args );
        while ($query->have_posts()) : $query->the_post();
        $ugly_time = get_post_meta($post->ID, 'mbs_thedate', 'true');
        $pretty_time = date('m/d/y', $ugly_time);
        echo '<h3 class="event-title">';
        echo '<a href="' . the_permalink() . '">' . the_title() . '</a>';
        echo '<small class="event-sidebar-date">';      
        echo $pretty_time;
        echo '</small>';
        echo '</h3>';
        endwhile;

The Meta Box

Since we've previously stored our date as a unix timestamp, we need to convert it back to a more human-readable format. So we've used the date() function to take our timestamp and turn it back into our $pretty_time format; m/d/y.

To sum up, we now have a custom post type that contains a custom field meta box to hold the date, and a useful way to sort our events which makes sense to the user. This custom post type could easily be extended to include more information as needed.

 

 


Comments

Hey,
Thank you for sharing! I've been looking for something like that for wordpress for ages now. Best solution so far!

Thanks again,
Max

Great but are you sure it will also sort the event on the same day but at different time? Can you explain?

The query is not set up to sort by the time of the event as well. To do that, you would likely add an additional array to meta_query which would look something like this:

1
2
3
4
5
 array(
   'key' => 'mbs_thetime',
   'value' => time(),
   'compare' => '>=',
   ),

This is untested, but it should point you in the right direction. You would also have to convert the field 'mbs_thetime' to a unix time stamp in similar fashion to the way we converted the date picker field.

I have spent hours trying to work out how to get this to work but no luck.

I wish you could have just posted the code needed to implement the date picker instead of putting 'Prerequisites' at the top. I have allot of experience with Wordpress but this does not make sense...

I'm grumpy and tired and I want a cupcake :( anguish

Thanks Paul for this useful information, while I'm not going to be making anyone a cupcake here, I will say that the Meta Box script linked from this post does provide an update for extensions, and has answered Lean's frustrations with a sample meta box extension class & method. Using the provided extension with Paul's tips provides a way for me to insert unix based time formatting to properly sort my CPT's by date.

Leannekera,

I share your frustrations. This is exactly what I need but can't get this to work. Numerous errors and problems have been overcome, but now i'm left with a meta-box that has a useless input box in it.

Any chance of the full list of code? Please?

Tim

nice

Is it possible to get events from two custom post types ? And order them by date ?

Alhamdulillah
Thank's Paul
Success