Custom Post Type Taxonomies

On Monday, we set up a custom post type in WordPress for listing events. We also added Event Tags to them, and created a template for showing off the individual events.

Today, we want to look at the overall Events page. We’re going to list events by the date they occur, and work back in our tags so users can get a list of only the events that apply to them.

In the demo, we’ll be listing some WordPress classes, and sorting them into Beginner, Intermediate and Advanced.

(We’re starting on blank files here, so if you just want to learn more about templates for taxonomies and post types, then read on! If you want to set this up on your site though, you’ll need to have done the steps in Events List with Custom Post Types first.)

1 – New Files for your Theme

We will be creating the archive page for showing all of our event posts together, and a similar page for showing the same list but with only the events that have our event tag on them.

That means we’ll need two new files in our theme folder, named in this format:

  • archive-post_type.php – In our case, this means archive-events.php.
  • taxonomy-tax_name.php – In our case, that will be taxonomy-event_tags.php.

However, our two templates are going to be extremely similar. Instead of duplicating our content between the two, let’s make it more efficient by using just the one file.

Create the taxonomy-event_tags.php file, and paste this into it:

1
<?php include_once 'archive-events.php'; ?>

Now when WordPress loads our event tag pages, it will use the template we’re about to make for the events list. Simple.

2 – The Barebones Template

The first thing to do with the archive-events.php file is make it match the rest of your theme.

The best way to do this is to make a copy of your theme’s index.php file, and then strip out everything from to . You’ll then add our code in exactly the place where all of that code used to be.

With the default 2010 theme, that leaves us with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php get_header(); ?>
 
<div id="container">
	<div id="content" role="main">
 
	<h1 class="page-title">
		Events
	</h1>
 
	<!-- OUR NEW CODE WILL GO HERE -->
 
	</div><!-- #content -->
</div><!-- #container -->
 
<?php get_sidebar(); ?>
<?php get_footer(); ?>

It may take a little fiddling to get this just right with your theme, but it’s important to get those in there to make sure your page layouts stay the same.

3 – List Our Event Tags

If you look at the screenshot, you’ll see a box at the top of the page that lists the event tags. And if the user is already filtering by a tag, it adds in a link back to the full events list.

Paste the following into your file and we’ll take a look at what it’s doing right after.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
 * List the Event Tags.
 */
 $tags = get_terms('event_tags', array(
 	'number' => 10,
 	'orderby' => 'name'
 ));
 
 // Build a comma-separated list of the tags.
 foreach($tags as $tag) :
 	$link = get_term_link($tag->slug, $tag->taxonomy);
 	$tagsText .= '<a href="'. $link .'">'. $tag->name .'</a>' . ', ';
 endforeach;
 
 // Strip out the ending ', '
 $tagsText = trim($tagsText, ', ');
?>

In this code, we get the tags dynamically (So no matter what tags you’re using, you can copy and paste the code above). If you had a huge number of event tags, you’d want to adjust the get_terms() line to get only the ones you’d like to display.

get_terms() gives us an array of tag objects. The foreach…endforeach; section loops through each one and adds it to a string (This is one of the times when our WordPress object cheat sheet comes in handy). The end result is a string like this:

Tag 1, Tag 2, Tag 3,

We then use PHP’s trim() function to take out that closing ‘, ‘.

So now we have all of our tags in a sentence in PHP. The next step is to output it.

1
2
3
4
<p class="events-info">
	Sort events by type: <?php echo $tagsText; ?>.
	<?php if(is_tax('event_tags') ) { echo ' Or <a href="'. get_bloginfo('url') .'/events/">view all</a>.'; } ?>
</p>

As you can see, we’re putting this all in a paragraph with the class “events-info” so that we can style it later on. Line 2 then prints out our list of tags from above.

The important part is line 3. We use a conditional tag to check if we are on an event tag page, and if we are, we add the simple link back to the main events page.

Still with me? Cool. Because the PHP is the easy part. Time to query the database now.

4 – The New Database Query

This query will be quite long, so let’s just work out what we want it to do in plain English first.

  1. Is this a taxonomy page? (i.e. being sorted by a tag)
  2. If it is, grab all of WordPress’ taxonomy tables too. Then make sure our results have the tag in them.
  3. Now carry on to grab only published Event posts with a Date field, and order them by that.

Here goes!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
/**
 * Do we need to filter by event tag?
 */
if(is_tax('event_tags') ) :
	$tag = strip_tags( get_query_var('event_tags') );
 
	$querystr = "
	    SELECT wposts.* 
	    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta, $wpdb->terms wterms, $wpdb->term_taxonomy wtax, $wpdb->term_relationships wrels
	    WHERE wposts.ID = wpostmeta.post_id
	    AND wterms.term_id = wtax.term_id
	    AND wtax.term_taxonomy_id = wrels.term_taxonomy_id
	    AND wrels.object_id = wposts.ID
	    AND wterms.slug = '$tag'
	    ";
 
else:
	$querystr = "
	    SELECT wposts.* 
	    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
	    WHERE wposts.ID = wpostmeta.post_id";
endif;
 
// Build the rest of the query, i.e. only get events with dates, and order newest first.
$querystr .= "
    AND wpostmeta.meta_key = 'Date'
    AND STR_TO_DATE(wpostmeta.meta_value,'%m/%d/%Y') >= CURDATE()
    AND wposts.post_status = 'publish'
    AND wposts.post_type = 'events'
    ORDER BY STR_TO_DATE(wpostmeta.meta_value,'%m/%d/%Y') ASC
    LIMIT 20
    ";

Told you it was long.

What we are doing here is building up our query as a PHP string. We start out by checking if this is a tag page (Exactly the same as in step 3, by using the is_tax() function).

If it is, then line 6 will get the tag’s slug (Similar to its name) and save it in the $tag variable.

Lines 8-15 start out our query, grab the 5 tables we need, and tell the database how to match them up. Line 15 then uses the event tag with all of that.

Lines 18-22 are for when we aren’t filtering by a tag, so they’re much shorter.

The rest (25-33) are the same for both queries, and take care of ordering the events by the date of the event (Not the date the post was written). For a full explanation, check out the original post in this series.

5 – (Last Step!) Listing Events

Onto the final stretch! We now have our query ready, so all we still need to do is run it and print the results.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$events = $wpdb->get_results($querystr, OBJECT);
 
if ($events):
	echo '<ul>';
 	foreach ($events as $post):
 		global $post;
 		setup_postdata($post); 
 
 		// Get a friendlier version of the date.
 		$date = get_post_meta($post->ID, 'Date', true);
 		$date = date_create($date);
 		$date = date_format($date, 'jS F, Y');
 		?>
 
	 	<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> - <?php echo $date; ?></li> 		
 	<?php endforeach;
 	echo '</ul>';
endif; ?>

Line 1 runs the query. If we get a result, we then start creating a list (Line 4). The foreach loop will run through each post brought back and create a list item for it (the setup_postdata() line let’s us use all of the template tags we’re used to working with).

Lines 9-12 are the same as we used in the last post to display the date in a more friendly format. Line 15 is where we eventually send the whole thing to the screen.

And that’s it all done! Well done if you made it this far. You should now have a fairly robust event listing that you can take and tweak to your own needs.

I’d love to hear your thoughts on the series so far. The next post is going to be about adding custom meta boxes to the admin panel (So you won’t need to use custom fields directly anymore here). Let me know if there’s anything else you’d like to see though!

Enjoy this post? You should follow me on Twitter!