WordPress, display looped posts by month name
Introduction
I had a requirement to build something like this for a personal site so thought I’d share what I came up with after reading a good answer on stackexchange that was very close to what I needed. I’m hoping that someone better versed with perf issues can get in touch and help me make this more efficient if possible.
An example of posts by month name
This is just a quick visual example to ensure we’re all “singing from the same hymn sheet” and you understand what I’m talking about.
June
- Lorem ipsum dolor sit amet
- Sit eaque placeat quo facilis odio vel debitis
- Aut facilis aspernatur 33 beatae veritatis id reiciendis
- …
May
- Ut molestias autem qui consequatur
- Dolor est dignissimos autem ab expedita voluptatum
- Ad itaque vero qui accusamus nihil
- …
April
So what we have is a loop in descending order where we’re printing the month name only once followed by all the posts that fall in that month. Simples.
Standard vs. custom queries
Depending on the loop type we need to take a slightly different approach. Both are used in the while() function but you’ll see the differences in the following. Hint: watch out for $wp_query & $query.
Standard query
In a normal query loop e.g. the one used in index.php we can use;
$current_month = get_the_date( 'F' );
if ( 0 === $wp_query->current_post ) {
the_date( 'F', '<h3>', '</h3>' );
} else {
$f = $wp_query->current_post - 1;
$old_date = mysql2date( 'F', $wp_query->posts[$f]->post_date );
if ( $current_month !== $old_date ) {
the_date( 'F', '<h3>', '</h3>' );
}
}
Custom query
In a custom query loop e.g. creating an archive page for a custom post type we can use the following which also includes the WP_Query() function and args.
$query = new WP_Query(
array(
'post_type' => 'my-custom-post-type',
'order' => 'DESC',
...
)
);
while ( $query->have_posts() ) {
$query->the_post();
$current_month = get_the_date( 'F' );
if ( 0 === $query->current_post ) {
the_date( 'F', '<h3>', '</h3>' );
} else {
$f = $query->current_post - 1;
$old_date = mysql2date( 'F', $query->posts[$f]->post_date );
if ( $current_month !== $old_date ) {
the_date( 'F', '<h3>', '</h3>' );
}
}
}
A brief explanation of what’s happening in both examples;
- Get the month name from the current post’s post date.
- Get the month name from the previous post’s post date.
- Compare the months names between those two posts.
- Display or do not display the month name depending on that comparison.
Mash them together into a partial
Rather than tediously copy these code segments into all your templates I’d suggest doing something similar to the following.
Step 01. Create PHP partial
Create a PHP partial file and paste in this code, give it a meaningful name e.g. posts-listing-by-month-name.php and then see the next step.
// Args.
$loop_type = $args['loop_type'];
$query = $args['query'];
// Vars.
$string_one = '<h3>';
$string_two = '</h3>';
$current_month = get_the_date( 'F' );
/**
* Standard or Custom Query?
*/
if ( 'standard' === $loop_type ) {
// Standard Query.
if ( 0 === $wp_query->current_post ) {
the_date( 'F', $string_one, $string_two );
} else {
$f = $wp_query->current_post - 1;
$old_date = mysql2date( 'F', $wp_query->posts[ $f ]->post_date );
if ( $current_month !== $old_date ) {
the_date( 'F', $string_one, $string_two );
}
}
} elseif ( 'custom' === $loop_type ) {
// Custom Query.
if ( 0 === $query->current_post ) {
the_date( 'F', $string_one, $string_two );
} else {
$f = $query->current_post - 1;
$old_date = mysql2date( 'F', $query->posts[ $f ]->post_date );
if ( $current_month !== $old_date ) {
the_date( 'F', $string_one, $string_two );
}
}
}
Step 02. Call the partial with appropriate args
Standard
If the template you are working on requires a ‘standard’ query, like found in index.php use the code below.
get_template_part(
'template-parts/partials/posts-listing-by-month-name',
null,
[
'loop_type' => 'standard',
'query' => '',
]
);
This uses the WordPress function get_template_part() and passes standard as the `loop_type` arg thus invoking the first routine and leaves the blank as is not required.`query` arg
Custom query
If the template you are working on requires a ‘custom’ query routine, like found in a custom post type’s archive page use the code below.
get_template_part(
'template-parts/partials/posts-listing-by-month-name',
null,
[
'loop_type' => 'custom',
'query' => $query,
]
);
Again we’re using the get_template_part() function but this time passing in custom as the and `loop_type` arg$query for query``arg. The $query var is defined when adding WP_Query() (see below as an example) – if you wished to use $q instead, just pass that in as the query``arg.
$query = new WP_Query(
[
'post_type' => 'my-custom-post-type',
'order' => 'DESC',
...
]
);
That’s all, you now have a basic working template
There are obviously many more parts you can add once you’ve got the basics working, adding an optional ‘show_year’ could be useful.
You can also edit the HTML before and after the month name is displayed allowing a good deal of flexibility by amending the contents of the $string_one and $string_two vars which are used by the_date() function.
Final thoughts
I’d been scratching my head for a while trying to figure this out, several attempts at searching provided that good answer on stackexchange that I’m grateful for, I just tidied it up and made it easier to use.
Have a play, have fun and if you do think of a way of improving it, please do get in touch and let me know.
// End of Article
Article Information
Further Reading
- Original post on stackexchange (wordpress.stackexchange.com)
- WordPress get_template_part() docs (developer.wordpress.org)
- WordPress mysql2date() docs (developer.wordpress.org)
- WordPress the_date() docs (developer.wordpress.org)