Article
Implementing post series in WordPress
Like most of the available plugins we will make use of a custom taxonomy to list all series and there episodes that are associated with a viewed post. We will also look after the taxonomy template and pagination.
Register Taxonomy
First of all we have to set up a hierarchical taxonomy in our function.php
. It will work like Categories, grouping posts together by sorting them into various series. I didn't included multilingual support for better readability but you can get help by generating this code with GenerateWP.
function my_taxonomy_series() { $labels = array( 'name' => 'Series', 'singular_name' => 'Series', 'menu_name' => 'Series', 'all_items' => 'All Series', 'parent_item' => 'Parent Series', 'parent_item_colon' => 'Parent Series:', 'new_item_name' => 'New Series Name', 'add_new_item' => 'Add New Series', 'edit_item' => 'Edit Series', 'update_item' => 'Update Series', 'view_item' => 'View Series', 'separate_items_with_commas'=> 'Separate series with commas', 'add_or_remove_items' => 'Add or remove series', 'choose_from_most_used' => 'Choose from the most used', 'popular_items' => 'Popular Series', 'search_items' => 'Search Series', 'not_found' => 'Not Found', 'no_terms' => 'No series', 'items_list' => 'Series list', 'items_list_navigation' => 'Series list navigation' ); $args = array( 'labels' => $labels, 'hierarchical' => true, 'public' => true, 'show_ui' => true, 'show_admin_column' => true, 'show_in_nav_menus' => true, 'show_tagcloud' => false ); register_taxonomy( 'series', 'post', $args ); } add_action( 'init', 'my_taxonomy_series', 0 );
Now we have a new custom taxonomy of the type post
and the name Series
and you can begin to create and manage series under "Posts/Series" in your WordPress backend. Adding posts to a series works like it does with categories, just choose from the Series Metabox within your post.
Basic Function
To generate a list of series parts we insert the function my_series_parts()
in our function.php
. Note that the number of series items is limited by the WordPress backend settings under "Settings/Reading/Blog pages show at most", so keep in mind to adjust the count if necessary.
function my_series_parts() { $output = ''; $postId = get_the_ID(); $postTerms = get_the_terms( $postId, 'series' ); if ( $postTerms && !is_wp_error( $postTerms ) ) { $postTerm = array_shift( $postTerms ); $output = '<div class="series"><p>This post is part of the ' . esc_html( $postTerm->name ) . ' Series</p><ol>'; $postQuery = new WP_Query( array( 'series' => $postTerm->slug ) ); while ( $postQuery->have_posts() ) { $postQuery->the_post(); $output .= sprintf( '<li%1$s><a href="%2$s">%3$s</a></li>', ( $postQuery->post->ID == $postId ? ' class="current-series-item"' : '' ), esc_url( get_permalink() ), esc_html( get_the_title() ) ); } $output .= '</ol></div>'; wp_reset_postdata(); } return $output; }
Line 3: Get the identification number of the viewed post.
Line 4: Get all series from the taxonomy series that the viewed post is part of. See: wordpress/get-the-terms
Line 6: If the variable $postTerms
contains false
or WP_Error
the function will return an empty string which is declared in line 2. That means the viewed post isn't part of a series. If $postTerms
contains terms, go on.
Line 7: Because a post is part of usually not more than one series and $postTerms
contains at least one multidimensional array we fetch the first one with array_shift()
.
Line 8: Adding to the variable $output
an open grouping tag, text that includes the series name and an open ordered list tag.
Line 10: That shorthand code retrieve posts based on the taxonomy query variable but it only works with slug. See: wordpress/Querying-by-taxonomy
Line 11, 12: Loop through the posts that are part of the series. See: wordpress/Standard-Loop
Line 13: Adding the linked list item.
Line 14: The linked list item string with 3 placeholders and type specifiers for line 15 till 17.
Line 15: If the looped post is the viewed post, add class="current-series-item
for CSS styling.
Line 16, 17: Get the link and the title of the post.
Line 21: Restore the $post variable. See: wordpress/wp-reset-postdata
Line 23: Returns the complete series markup if the viewed post is part of a series or an empty string.
Function Call
For displaying the list of series parts we have to call and echo the function my_series_parts()
somewhere within single.php
or a similar template.
<aside> <p>Example Markup.</p> <div> <?php the_category(); ?> </div> <?php echo my_series_parts(); ?> </aside>
Pagination
If you want to navigate to the next and previous episode of the post include the function the_post_navigation()
in our single.php
.
<?php the_post_navigation( array( 'prev_text' => '« Previous Episode', 'next_text' => 'Next Episode »', 'in_same_term' => true, 'taxonomy' => 'series', 'screen_reader_text'=> 'Series navigation' ) ); ?>
Line 5, 6: Navigate only between series parts.
You can place the function at the bottom of the post but differentiate it visually with CSS from the normal pagination. See: wordpress/the-post-navigation
Advanced Function
The additional code will generate lists for all series if a post is part of more than one. It also changes the items order so that the first series part appears on top of the list. In that example I use a description list in the html markup and link to the series taxonomy.
function my_series_parts() { $output = ''; $postId = get_the_ID(); $postTerms = get_the_terms( $postId, 'series' ); if ( $postTerms && !is_wp_error( $postTerms ) ) { $output = '<div class="series"><p>Part of the Series</p><dl>'; foreach ( $postTerms as $postTerm ) { $output .= sprintf( '<dt><a href="%1$s">%2$s</a></dt>', esc_url( get_term_link( $postTerm->term_id, $postTerm->taxonomy ) ), esc_html( $postTerm->name ) ); $postQuery = new WP_Query( array( 'order' => 'ASC', 'tax_query' => array( array( 'taxonomy' => $postTerm->taxonomy, 'field' => 'term_id', 'terms' => $postTerm->term_id ) ) ) ); while ( $postQuery->have_posts() ) { $postQuery->the_post(); $output .= sprintf( '<dd%1$s><a href="%2$s">%3$s</a></dd>', $postQuery->post->ID == $postId ? ' class="current-series-item"' : '', esc_url( get_permalink() ), esc_html( get_the_title() ) ); } wp_reset_postdata(); } $output .= '</dl></div>'; } return $output; }
Line 9: In contrary to the basic function this will loop each series or rather array.
Line 12: Get the series link. See: wordpress/get-term-link
Line 15: Query taxonomy and declare parameters. See: wordpress/Taxonomy-Parameters
Line 17: Ascending order from oldest to newest. See: wordpress/Order-Parameters
Line 21, 22: Getting terms by ID and not slug like in the basic function what makes it more reliable.
Taxonomy Template
To make use of the linked taxonomy in the series list we need to create another template because now the output is handled by index.php
. So you can just copy index.php
or category.php
and name it taxonomy.php
.
<div> <p>Example Markup.</p> <?php echo category_description(); ?> <p>Series</p> <ul> <?php wp_list_categories( array( 'current_category' => get_queried_object_id(), 'hide_empty' => 1, 'orderby' => 'name', 'separator' => '', 'show_option_none' => '', 'style' => 'list', 'taxonomy' => 'series', 'title_li' => '', 'use_desc_for_title'=> 0 ) ); ?> </ul> </div>
Line 3: For displaying the series description you can use category_description()
.
Line 7: List all series with wp_list_categories()
. See: wordpress/wp-list-categories
Line 8: Get the current series item with get_queried_object_id()
. See: wordpress/get-queried-object-id
Line 14: Declare the taxonomy.
Conclusion
It maybe seems that a lot of coding has to be done but the only none standard code is the function my_series_parts()
, the rest are basic WordPress features. You can build your preferred function out of the two described examples.