Let’s say you have taken over a website for a podcast. Each time a episode of this podcast is produced, a WordPress post is published that provides information about that episode. There are over 500 episodes to date.

You’ve been tasked with displaying the podcast episode number in the podcast archives, and you’d rather not manually add custom meta for each of the 500+ episodes.

We’ll show you how to:

  1. Get the total count of published posts in a certain post type.
  2. Count backwards as you iterate through the WordPress loop.
  3. Adjust the episode number accordingly through paged archives.
  4. BONUS: Make it work with Jetpack Infinite Scroll too.

This is the exact same effect I use on the Themetry website to give visitors a visual idea of what number tutorial they’re on in the index.

Let’s begin!

Getting started

To follow along, you’ll need the following:

  • A WordPress site, preferably a locally hosted one
  • A custom post type with the slug of ‘podcast’ with has_archive set to true
  • A bunch of posts and podcasts saved in various post statuses (mostly ‘publish’)
  • The Jetpack plugin for Infinite Scroll, Development Mode can be turned on

A deep-dive into wp_count_posts

Say hello to the wp_count_posts function.

With it, we can glean all sorts of information about the total number of posts of a certain post type.

Get total count of all posts, regardless of post status

$post_count = wp_count_posts();
echo "<p>Total number of posts (all post statuses): $post_count</p>";

This won’t be of use to us, because we want to count the ‘podcast’ post type. This just gives us the count of the ‘post’ post type.

Also, we only want to count published posts. A site with drafts would screw up the count because drafts are not (typically) publicly displayed.

Another way of counting all posts, regardless of post status

$post_count_alternative = wp_count_posts( 'post' );
echo "<p>Total number of posts: $post_count</p>";

Same result as above. We’re just specifying the post type this time around.

Since ‘post’ is the default, we don’t need to specify it in this case. But we’ll need to remember how to specify post types when we get to podcasts.

Get total count of all posts, published only

$post_count_published = wp_count_posts()->publish;
echo "<p>Total number of posts (just published): $post_count_published</p>";

Appending ->publish to wp_count_posts() ensures we’re only counting published posts.

Just for kicks, you could change the ->publish to ->draft, ->pending, or ->trash.

But the counts of drafts, pending, and trashed posts will be useless for our purposes.

Get total count of all podcasts, published only

Let’s combine what we learned above to get what we’re really after: a total count of published podcasts.

$podcast_count = wp_count_posts( 'podcast' )->publish;
echo "<p>Total number of podcasts (just published): $podcast_count_published</p>";

We’ll use this $podcast_count value from here on out.

Count backwards through the loop

Now that we know how to count the number of published podcasts, we’ll use that in our podcast-related template files.

In the podcast archive template

Open up your archive-podcast.php file.

Before the while loop starts, we’ll place our initial published podcast count value.

// Get total count of published podcasts
$podcast_count = wp_count_posts( 'podcast' )->publish;

// Start loop
while ( have_posts() ) : the_post();

	// Use include( locate_template() ) so we can pass variable values to template part
	include ( locate_template( 'template-parts/content-podcast.php' ) );

	// Decrease counter

// End loop

Within our while loop, we’ll include the template-parts/content-podcast.php template.

It is critical that we use include ( locate_template() ) instead of get_template_part(), as the latter doesn’t allow variables (like $podcast_count) to be passed through it.

After the template is included, we’ll decrease the $podcast_count value by one.

In the podcast content template part

Open up your template-parts/content-podcast.php template.

We’ll output the count in a data attribute.

<article class="hentry" data-count="<?php echo $podcast_count; ?>">
	<?php the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' ); ?>
</article><!-- .hentry -->

We’ll then use CSS to grab the number and display it.

.podcast-list .hentry:after {
	content: "Episode #: " attr(data-count);

Although the template part is pretty small, we’re still separating it into its own file so we can re-use it for our Jetpack Infinite Scroll render function later on.

Getting it to work with pagination

At this point, our programmatic podcast episode numbering is working great.

That is, until you navigate beyond the first page of the podcast archive (i.e. example.localhost/page/2/)

You’ll notice the count on the second page displays as if it were still on the first. And why would it? We have not made our counting function aware of what page we’re on.

Let’s revisit our archive-podcast.php file.

Get the page number

First, let’s find out what page number we’re on by pasting this before the loop:

// Get page number
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

On the second page, the value of $paged will be 2. On the third page, the value of $paged will be 3. And so on.

Get the posts per page

Next, let’s fine out how many posts there are per page.

// Get posts per page option
$posts_per_page = get_option( 'posts_per_page' );

For this, we’re just grabbing the option set in Settings → Reading.

Adjusting the podcast episode number on pagination

To recap, we now have three variables defined before the while loop starts:

  • Published podcast count
  • Current page number
  • Posts per page

Let’s say we have 100 podcasts, 10 posts per page, and we’re on the 2nd page. That means, the 2nd page of podcasts should start with episode #90.

Before peeking ahead, think back to algebra class. How can we use these values to adjust the podcast episode number on the second page?

Adjusted podcast count = Published podcast count - ( ( Current page number - 1 ) * Posts per page )

And now let’s plug in our sample values:

90 = 100 - ( ( 2 - 1) * 10 )

With PHP, we can write our “plain English” equation the same way.

Final variables and equation

For convenience, here are our variables and equation grouped together. To make things easier to read, we’ve handled the “current page number – 1” part earlier.

// Get total count of podcasts
$podcast_count = wp_count_posts( 'podcast' )->publish;

// Get page number minus 1
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$paged = $paged - 1;

// Get posts per page option
$posts_per_page = get_option( 'posts_per_page' );

// Adjusted podcast count
$podcast_count = $podcast_count - ( $paged * $posts_per_page );

At this point, we have a functioning podcast episode number displaying correctly on the first page, and subsequent pages.

But what if we want to use Infinite Scroll? That will require a little extra work.

Making it work with Jetpack Infinite Scroll

To make it work, we’ll need to declare theme support (necessary for any Infinite Scroll integration) and define a render function (this is where the counting magic will happen).

Add theme support

In our blank inc/jetpack.php file, add the following to declare Infinite Scroll support.

function themetry_jetpack_setup() {
	// Add theme support for Infinite Scroll.
	add_theme_support( 'infinite-scroll', array(
		'container' => 'entries-list',
		'render'    => 'themetry_infinite_scroll_render',
		'footer'    => 'page',
		'wrapper'   => false,
	) );
add_action( 'after_setup_theme', 'themetry_jetpack_setup' );

The container value matches up with the container ID in archive-podcast.php and index.php. Since we want Infinite Scroll to work on both normal posts and podcasts, we want to keep this pretty standard.

We’ll save the podcast-specific stuff for the render function, which we have called but yet to define. We will do so now.

Add render function

Since we want to make sure Infinite Scroll works with non-podcast posts as well, we’ll use a conditional function to isolate the podcast-specific code.

function themetry_infinite_scroll_render( $args ) {
	// Get total count of posts
	$podcast_count = wp_count_posts( 'podcast' )->publish;

	// Get posts per page option
	$posts_per_page = get_option( 'posts_per_page' );

	// Get page number
	$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

	// New post count = total post count - ( page number * posts per page )
	$podcast_count = $podcast_count - ( $paged * $posts_per_page );

	while ( have_posts() ) {

		if ( is_post_type_archive( 'podcast' ) {
			// Must use locate_template so we can pass $podcast_count value
			include ( locate_template( 'template-parts/content-podcast.php' ) );
			// Decrease counter
		} else {
			get_template_part( 'template-parts/content' );

Note that the themetry_infinite_scroll_render function name matches with what we set as our ‘render’ key value in our theme support declaration above.

Notable difference between the paged archive code

The Infinite Scroll render function is very similar to the code used on podcast-archive.php, with one notable difference: we don’t subtract 1 from the page number.

For some reason, $paged in our Infinite Scroll render function gives us the page we were on before we loaded new posts. Subtracting 1 on top of that would give us inaccurate counts.

Not a replacement for steps completed thus far

It’s also worth noting that an Infinite Scroll render function is not a replacement for any of the steps we’ve completed thus far.

The “Count backwards through the loop” step is still used to do the initial counting when a user lands on the podcast archive page (i.e. https://example.com/podcast/).

The “Getting it to work with pagination” step is still used because it’s possible for users to land on paged archive URLs (i.e. https://example.com/podcast/page/2/) even with Infinite Scroll enabled.

Wrapping up

While we used a custom post type as an example, you could modify the code to work with taxonomies as well. If you’re unsure how exactly, ask in the forum and I’ll help.

But be wary of applying this technique on a broad scale across your site.

If you tried to apply it to your entire blog, and then individual categories, post order numbers would be different depending on the view, and that would be confusing.

Next Article
Cache-busting enqueued scripts and styles in WordPress