Advertisement
Plugins

Adding Post Series Functionality to WordPress With Taxonomies

by

Ever wrote a "post series" on your blog? If you did, you probably needed to add the links of the other parts of the series into the latest post you wrote. Each time you finished a new part, you had to update the link list of the other parts. There has to be an easier way, right?

There is. In this article, we're going to learn how to create the "post series" functionality by using the the taxonomies and shortcodes.

The article contains 3 parts:

  • Creating the taxonomy
  • Creating the shortcode
  • Adding the taxonomies and using the shortcode

I'll explain almost every line of code, so you can develop it any way you like.


Step 1 Creating the Plugin File

This barely counts as a step: We'll just create the plugin file and fill in the details of the plugin:

<?php
/*
Plugin Name: Post Series
Plugin URI: http://wp.tutsplus.com/tutorials/plugins/adding-post-series-functionality-to-wordpress-with-taxonomies/
Description: Adds the "post series" functionality to WordPress with the help of a taxonomy and a shortcode.
Version: 1.0
Author: Barış Ünver
Author URI: http://beyn.org/
*/
?>

Save this little chunk of code in the file post-series.php or any name you like, into the folder /wp-content/plugins/post-series/.


Step 2 Setting Up Our New Taxonomy: "Series"

WordPress has a not-so-new functionality to create "taxonomies" so we can create more kinds of "classification groups" just like tags or categories (which are the built-in taxonomies of WordPress). We're going to set up a new taxonomy called "Series" and we'll be using this new taxonomy in our post series to help us.

First, we need to create a function to register the taxonomy. Let's call it series_tax:

function series_tax() {
	/* Creating an empty function LIKE A BOSS */
}
add_action('init', 'series_tax', 0);

Take note that we also added an action to make the function run when WordPress is ready.

Next, we'll be using the native register_taxonomy() function to create our taxonomy. But before that, we should set up the "labels" of the taxonomy:

function series_tax() {
	$labels = array(
		'name' => _x('Series', 'taxonomy general name'),
		'singular_name' => _x('Series', 'taxonomy singular name'),
		'all_items' => __('All Series'),
		'edit_item' => __('Edit Series'),
		'update_item' => __('Update Series'),
		'add_new_item' => __('Add New Series'),
		'new_item_name' => __('New Series Name'),
		'menu_name' => __('Series')
	);
}
add_action('init', 'series_tax', 0);

We didn't use all the labels – You can find a full reference of taxonomy labels in our article Taking WordPress Custom Taxonomies to the Next Level.

We can register the taxonomy now:

// create the "Series" taxonomy for posts only
function series_tax() {
	$labels = array(
		'name' => _x('Series', 'taxonomy general name'),
		'singular_name' => _x('Series', 'taxonomy singular name'),
		'all_items' => __('All Series'),
		'edit_item' => __('Edit Series'),
		'update_item' => __('Update Series'),
		'add_new_item' => __('Add New Series'),
		'new_item_name' => __('New Series Name'),
		'menu_name' => __('Series')
	);

	register_taxonomy(
		'series',
		array('post'), /* if you want to use pages or custom post types, simply extend the array like array('post','page','custom-post-type') */
		array(
			'hierarchical' => true, /* if set to "true", you can use Series as categories; if set to "false", you can use them as tags! */
			'labels' => $labels,
			'show_ui' => true,
			'query_var' => true,
			'rewrite' => array('slug' => 'series'), /* you may need to flush the rewrite rules at Options -> Permalinks (just update the existing preferences without any change) */
		)
	);
}
add_action('init', 'series_tax', 0);

Congratulations, you just created the first half of your plugin! Now, let's get to the second half...


Step 3 Creating the Shortcode: [series]

In this step, we're going to build the [series] shortcode. With this shortcode, we'll be able to insert the list of the series' posts. We'll also be able to customize the shortcode with some attributes like "title", "title_wrap", "slug", "id", "list", "limit" and "future" which will all be optional.

Let's start by creating our function:

function series_sc($atts) {
	extract(
		shortcode_atts(
			array(
				"slug" => '',
				"id" => '',
				"title" => '',
				"title_wrap" => 'h3',
				"list" => 'ul',
				"limit" => -1,
				"future" => 'on'
			),
			$atts
		)
	);
}
add_shortcode('series','series_sc');

We now have an empty shortcode with 7 optional attributes! We'll get to know them while writing the rest of the code but I should explain them now:

  • slug – The slug of the series, defaults to nothing
  • id – The ID of the series, defaults to nothing
  • title – The title of the output, defaults to nothing
  • title_wrap – The HTML tag to wrap the title with, defaults to "h3"
  • list – The HTML tag to wrap the post list with, defaults to "ul"
  • limit – The maximum number of posts, defaults to -1 (all posts)
  • future – The option to include the future posts or not, defaults to "on"

Next, we'll code the "find the proper taxonomy" part:

if ($id) {
	// Use the "id" attribute if it exists
	$tax_query = array(array('taxonomy' => 'series', 'field' => 'id', 'terms' => $id));
}
elseif ($slug) {
	// Use the "slug" attribute if "id" does not exist
	$tax_query = array(array('taxonomy' => 'series', 'field' => 'slug', 'terms' => $slug));
}
else {
	// Use posts own Series tax if neither "id" nor "slug" exist
	$terms = get_the_terms($post->ID,'series');
	if ($terms && !is_wp_error($terms)) {
		$tax_query = array(array('taxonomy' => 'series', 'field' => 'slug', 'terms' => $term[0]->slug));
	}
	else {
		$error = true;
	}
}

The "slug" and the "id" work in cooperation but they're both optional: The shortcode will first look for the "slug", then it'll look for the "id". If there's no slug and no ID either, the shortcode will try to get the "series" taxonomy of the post that contains the shortcode.

Now, let's code the "create the title (if it's specified)" part:

if ($title) {
	// Create the title if the "title" attribute exists
	$title_output = '<'.$title_wrap.' class="post-series-title">'.$title.'</'.$title_wrap.'>';
}

There will be no title if the "title" attribute is not specified. There will be a title with the <h3> tag if the "title_wrap" attribute is not specified. You should change the default of the attribute to something else (like <h4>) if you use something else as subheadings.

Let's get to the "display the future posts or not" part now:

if ($future == 'on') {
	// Include the future posts if the "future" attribute is set to "on"
	$post_status = array('publish','future');
}
else {
	// Exclude the future posts if the "future" attribute is set to "off"
	$post_status = 'publish';
}

I strongly recommend that you leave the "future" attribute "on" but if you don't want to list the titles of your future posts (not drafts), you can set it to "off".

All right, we finished coding the attribute parts (except "limit"). We can now get to the part where we get the posts:

if ($error == false) { /* We are going to close this later */
	$args = array(
		'tax_query' => $tax_query,
		'posts_per_page' => $limit,
		'orderby' => 'date',
		'order' => 'ASC',
		'post_status' => $post_status
	);
	$the_posts = get_posts($args);
  • The 'tax_query' argument is defined by the $tax_query variable which we created just a minute ago in the "find the proper taxonomy" part.
  • The 'posts_per_page' argument is defined by the "limit" attribute of the shortcode.
  • The 'orderby' and 'order' arguments are defined to get the posts listed chronologically – the newest post will be at the bottom of the list.
  • The 'post_status' argument is defined by the $post_status variable which we also created above, in the "display the future posts or not" part.

Now we're done with fetching the posts, we can put everything together and create the post list!

	/* if there is more than one post with the specified "series" taxonomy, display the list. if there is just one post with the specified taxonomy, there is no need to list the only post! */
	if (count($the_posts) > 1) {
		// display the title first
		$output = $title_output;
		// create the list tag - notice the "post-series-list" class
		$output .= '<'.$list.' class="post-series-list">';
		// the loop to list the posts
		foreach ($the_posts as $post) {
			setup_postdata($post);
			if ($post->post_status == 'publish') {
				$output .= '<li><a href="'.get_permalink($post->ID).'">'.get_the_title($post->ID).'</a></li>';
			}
			else {
				/* we can not link the post if the post is not published yet! */
				$output .= '<li>Future post: '.get_the_title($post->ID).'</li>';
			}
		}
		wp_reset_query();
		// close the list tag...
		$output .= '</'.$list.'>';
		// ...and return the whole output!
		return $output;
	}
} /* Remember the "if" we did not close? :) */

And the final code of our shortcode's function will be like this:

// The shortcode function of Post Series
function series_sc($atts) {
	extract(
		shortcode_atts(
			array(
				"slug" => '',
				"id" => '',
				"title" => '',
				"title_wrap" => 'h3',
				"list" => 'ul',
				"limit" => -1,
				"future" => 'on'
			),
			$atts
		)
	);
	if ($id) {
		// Use the "id" attribute if it exists
		$tax_query = array(array('taxonomy' => 'series', 'field' => 'id', 'terms' => $id));
	}
	elseif ($slug) {
		// Use the "slug" attribute if "id" does not exist
		$tax_query = array(array('taxonomy' => 'series', 'field' => 'slug', 'terms' => $slug));
	}
	else {
		// Use posts own Series tax if neither "id" nor "slug" exist
		$terms = get_the_terms($post->ID,'series');
		if ($terms && !is_wp_error($terms)) {
			$tax_query = array(array('taxonomy' => 'series', 'field' => 'slug', 'terms' => $term[0]->slug));
		}
		else {
			$error = true;
		}
	}
	if ($title) {
		// Create the title if the "title" attribute exists
		$title_output = '<'.$title_wrap.' class="post-series-title">'.$title.'</'.$title_wrap.'>';
	}
	if ($future == 'on') {
		// Include the future posts if the "future" attribute is set to "on"
		$post_status = array('publish','future');
	}
	else {
		// Exclude the future posts if the "future" attribute is set to "off"
		$post_status = 'publish';
	}
	if ($error == false) {
		$args = array(
			'tax_query' => $tax_query,
			'posts_per_page' => $limit,
			'orderby' => 'date',
			'order' => 'ASC',
			'post_status' => $post_status
		);
		$the_posts = get_posts($args);
		/* if there is more than one post with the specified "series" taxonomy, display the list. if there is just one post with the specified taxonomy, there is no need to list the only post! */
		if (count($the_posts) > 1) {
			// display the title first
			$output = $title_output;
			// create the list tag - notice the "post-series-list" class
			$output .= '<'.$list.' class="post-series-list">';
			// the loop to list the posts
			foreach ($the_posts as $post) {
				setup_postdata($post);
				if ($post->post_status == 'publish') {
					$output .= '<li><a href="'.get_permalink($post->ID).'">'.get_the_title($post->ID).'</a></li>';
				}
				else {
					/* we can not link the post if the post is not published yet! */
					$output .= '<li>Future post: '.get_the_title($post->ID).'</li>';
				}
			}
			wp_reset_query();
			// close the list tag...
			$output .= '</'.$list.'>';
			// ...and return the whole output!
			return $output;
		}
	}
}
add_shortcode('series','series_sc');

Conclusion: Usage Examples

Usage is pretty simple:

  1. Create your "series" from the Posts » Series page (as if you're creating new categories),
  2. Assign your posts to those "series" while writing (as if you're choosing categories),
  3. Use the [series] shortcode wherever you want!

You probably noticed this: You can use the shortcode without any attributes – if you're using it inside your series' posts:

[series]

But if you want to include series in pages or in a post which is not assigned to the series you're including, you can use the "slug" or the "id" attributes:

[series slug="wordpress-themes"]
[series id="146"]

Finally, you can limit the number of posts shown, you can set a title and its heading tag, you can change the list to an ordered (numbered) list and you can prevent the future posts to be shown:

[series title="More WordPress Theme Lists" title_wrap="h4" limit="5" list="ol" future="off"]

I don't want to paste the 100+ lines of the full source code here (the tutorial is already long) but you can download the plugin we created from here. Hope you like it!

Related Posts
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Creating Custom Queries for AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the second in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options.Read More…
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Assigning Categories and Taxonomy Terms to AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the first in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options. Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML - Creating a Page TemplateCreating wordpress theme from html 400
    So far in this series, I've shown you how to create a fully functioning WordPress theme from static HTML. We've covered the following steps: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file. At the moment, your theme only has one template file for displaying content—the index.php file. A powerful feature of WordPress is the ability to use template files for different kinds of content.Read More…
  • Code
    Plugins
    Create a Shortcode to List Posts With Multiple ParametersPost listing shortcode main image400
    On many of the client sites I build, I find there are times when I need to include a post listing on a page. I'm not talking about archive pages here, but adding a custom listing to an existing static page. For example, I might want to list some posts on the 'About' page, or the site may require an in-depth page for a topic, with a list of posts and custom post types related to that topic. One way to do this is by creating a custom page template to include the current page content plus the results of a second custom query, but if you want more flexibility over how you list posts, or just want to do it a few times, a shortcode will be a simpler solution. In this tutorial, I'll show you how to create a simple shortcode to list all posts of a custom post type, and then I'll expand on that to create a shortcode with various parameters that users can specify to list posts however they want.Read More…
  • Code
    Plugins
    Integrating Multiple Choice Quizzes in WordPress – Creating the FrontendIntegrating multiple choice quizzes in wordpress
    This is the second part of the series on developing a multiple choice quiz plugin for WordPress. In the first part we created the backend of our plugin to capture the necessary data to store in the database. In this final part, we will be creating the frontend of the plugin where the users can take quizzes and evaluate their knowledge.Read More…
  • Code
    Plugins
    Multiple Shortcodes With a Single Function: 3 Killer ExamplesMultiple shortcodes with a single function 3 killer examples
    The powerful Shortcode API allows us to create "snippets" and include special content (such as PHP functions or complex HTML code) in our posts. It's extremely common to build separate functions for each shortcode, but is it really necessary? In this post, we're going to see how we can utilize more than one shortcode by building a single function.Read More…