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'         => '&laquo; Previous Episode',
        'next_text'         => 'Next Episode &raquo;',
        '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.

Leave the first comment

  • required
  • required, will not be published