How to group Views results by year and month
Our mission today is to use a View, to display a list of posts, grouped by month and year. We’ll show how to do this, using a View and some PHP, in a custom shortcode.
We start with a custom post type called ‘News’. The ‘News’ post type, like any other post in WordPress already has a date field. We’ll use this date for our grouping.
The first step is to create the View, which will display the list of news. If you are new to Views, have a look at the documentation for creating your first View.
The key thing, in our solution, is to order the results by their dates. We set this using the ‘order by’ field in the View editing page.
- Go to Views in the WordPress dashboard
- Create a new View for displaying content of type ‘News’
- Order by post date, descending
- Add the fields you want to display to the View layout
The Layout HTML for our example View is:
<h4>[wpv-post-date format="Y"]</h4> <h5>[wpv-post-date format="F"]</h5> <h4>[wpv-post-title]</h4> [wpv-post-excerpt]
Save the View, insert it to a page and see the output.
As expected, we get the ‘news’ items, one after the other, each with its date title. We do not want to have the year and the month repeated for each news. We want to have it displayed only once and the corresponding news grouped under it.
For this we will need some customization. Our View already returns the news items according to their dates. We will create a custom shortcode, which will display the date heading only once, for all the news items that belong to that date.
Here is the source of our shortcode:
//group by month and year add_shortcode('heading', 'my_heading'); function my_heading($atts, $content = '') { static $year = null; static $month = null; $condition = $atts['condition'];; $value = $atts['value'];; switch ($condition) { case 'year': case 'month': if ($$condition != $value) { $$condition = $value; return $content; } break; } return ''; }
The $content is what the shortcode filters. We will pass the date heading to it. The $atts argument is an array, with the arguments we will feed.
This function will output each heading once. If it sees the same heading again, it will output nothing for it. We store the year and month memory as static variables, so that they are kept as this shortcode is called for different posts.
To add this shortcode to your site, you can copy/paste the PHP clip and add to your site’s functions.php file.
Let’s see how we use it in the View.
Edit the View Layout section and wrap the date shortcodes in our custom ‘heading’ shortcode.
[heading condition="year" value="[wpv-post-date format="Y"]"] <h4>[wpv-post-date format="Y"]</h4> [/heading] [heading condition="month" value="[wpv-post-date format="F"]"] <h5>[wpv-post-date format="F"]</h5> [/heading] <h4>[wpv-post-title]</h4> [wpv-post-excerpt]
Noticed what we’ve done? We call the ‘heading’ shortcode with the display conditions, ‘year’ and ‘month’. The value is the formatted year/month strings.
Remember that it outputs everything just once? As a result, we will see the year and month only once in the output, followed by all the posts that belong to that year and month.
The posts themselves are not filtered. All we do is filter the date, causing it to display once for each group of posts that follow.
Of course, for this to work, the posts must be ordered by date.
Want a challenge?
In this example, we explained how to write a custom shortcode that will group items, when they are sorted according to the headings.
Can you think of a way to group together articles if they come in a random order?
Hint: you can use a similar technique with shortcodes. Instead of filtering the dates, consider filtering the entire output and queuing it. Then, output every section together.
I realized a while back that writing shortcodes really augments what you can do with Views.
Keep these tutorials coming! Also, please add more shortcode to Views, even more obscure stuff.
For example, here are two shortcodes I have written that would be a great addition to Views core:
WP Views get wp_posts column data
Gets a a column from the wp_posts table by post ID.
Example 1:
[wpv-get-posts-field id=”10″ field=”guid”]
Example 2
[wpv-get-post-column id=”1″ column=”post_content”]
https://github.com/khromov/wp-wp-views-shortcode-get-posts-field/blob/master/wp-views-shortcode-get-posts-field.php
WP Views Get Attachment Image Shortcode
Prints the actual image URL for a Media post. Optionally you can specify the thumbnail size.
Example usage: [wpv-get-attachment-image id=”[wpv-post-id]” size=”thumbnail”]
https://github.com/khromov/wp-wp-views-shortcode-get-attachment-image/blob/master/wp-views-get-attachment-image.php
Hi Stanislav,
thanks for the feedback.
About the use of shortcodes they really are a very useful feature in WordPress and we use it a lot. For Views we have a all set of them and a some what recent addition is one for retrieving the featured image of a post [wpv-post-featured-image size="medium" raw="false"]
You can see more about Views shortcodes here https://toolset.com/documentation/views-shortcodes/
We plan on having a new tutorial each week, so keep posted! 😉
Hi Ana,
Your shortcode [wpv-post-featured-image] gets the featured image for the post. (Using get_the_post_thumbnail() )
My shortcode [wpv-get-attachment-image] is used when looping over the Media (attachment) post type to get the URL to the image in the attachment. It uses wp_get_attachment_image_src()
So there’s a difference between these two shortcodes.
Hi guys,
maybe jquery could be useful.
I think if we add a data attribute like data-year we can then group every item with the same value for the data attribute in a single div.
What about this?
Yes, you’re correct. You can also do this grouping with JS. The benefit of doing it in PHP is that Google will see it correctly. If you group items with JS, it will look correctly on the screen, but indexed differently by search engines.
You’re absolutely right!
If Toolset could have a shortcode or snippets handler plugin like http://wordpress.org/plugins/code-snippets/ then snippets could be packed together with a type and view in the module manager.
I second this. Having added lots of code to a custom function file, a snippet handler inbuilt into types and view would be a great addition.
I’ll Third this. I use “Post Snippets” which works great when views is too cumbersome. But if it were built into views it would possibly make things cleaner. I hate relying on going many different directions to achieve things. It’s messy and over time potentially confusing.
Hi, thanks for doing the tutorial, its exactly what I needed.
When I add the shortcode function I’m getting the following parse error.
Parse error: syntax error, unexpected ‘$value’ (T_VARIABLE) on line 8
Ok, forget my post above, I think I was making a mistake. Sorry about that.
HI Mark,
if you have any difficulty setting this example you can always open a ticket in the support forums we will help you there.
Hi Ana,
Thanks, I’ll keep playing and create a ticket if I get stuck, which is quite likely 🙂
Just wanted to let you know that these two lines require semi colons to work.
$condition = $atts[‘condition’]
$value = $atts[‘value’]
I was getting T_Variable errors with the code as is.
Please replace them with
$condition = $atts[‘condition’];
$value = $atts[‘value’];
Thanks this worked outstanding otherwise. Really polishes off the Awards page for my client.
Corrected. Thank yo uvery much William!
Any way to get this to work with a custom date field? I’d like to filter a CPT by Year and then show each year’s posts like this:
[SELECT YEAR DROPDOWN]
2015
JANUARY
Jan 1 – ITEM
Jan 1 – ITEM
Jan 8 – ITEM
Jan 14 – ITEM
Jan 14 – ITEM
FEBRUARY
Feb 7 – ITEM
Feb 8 – ITEM
Feb 8 – ITEM
MARCH
Mar 11 – ITEM
Mar 12 – ITEM
Mar 12 – ITEM
Mar 24 – ITEM
etc.
Thanks
the posts are already ordered by date so if you append the custom field shortcode to the item to display you will get an ordered list by date of the post.
In order to have an equivalent process but considering a custom field that saves a date. I would suggest you use the order by that custom field for the View and use the same custom field to pass the date to the custom function instead of the post date that is used in the above blog post.
Does this make sense?
Question. If the event has a few dates, how to group them by month? (That would be duplicated in each month).
Event 1 is the date of 12/01/16, 03/15/16
Event 2 is the date of 01/13/16, 14/03/16
How to make that conclusion was:
January:
Event 1
Event 2
March:
Event 1
Event 2