How to best group field values by date?
-
Please advice me on how to achieve this:
I have post type ‘Journal’. This post type has custom field ‘food’. I want to have a page that automatically updates itself whenever a post with value in the ‘food’ field is added, so it calculates the total amount of ‘food’ number values for both the month and the year.
For example I have 2 posts – Journal #1 and Journal #2. Journal #1 has Food: 2 value, Journal #2 has number 3 value. I need a page that says Month January 2023, food: 5; Year 2023, food: 5. Is it better to transform the custom fields into taxonomies and try to tackle the problem that way?
- This topic was modified 1 year, 11 months ago by youknowmenot. Reason: Fixed my 2nd problem
-
See WP_Query date parameters, especially year and monthnum, and get_post_meta().
The following PHP, when installed as a plugin for example at
wp-content/plugins/shortcode-food-journal/shortcode-food-journal.php
, or to your theme’sfunctions.php
, or to a Code Snippet plugin, will enable a shortcode[food-journal]
which does what I think you’re describing. It goes from the current month back to January 2000.2000
can be changed to another starting year on the lineforeach( range( date('Y'), 2000 ) as $year ) {
<?php /** * Plugin Name: Shortcode: Food Journal * Description: Output <code>food</code> counts per month and year with shortcode <code>[food-journal]</code>. * Plugin Author: ??δ??? * Author URI: https://pd.cm/ * Plugin URI: https://www.ads-software.com/support/topic/how-to-best-group-field-values-by-date/ * Version: 1 */ add_shortcode( 'food-journal', function( $atts, $content, $shortcode_tag ){ ob_start(); $base_query_args = [ 'post_type' => 'journal', 'post_status' => 'published', 'posts_per_page' => -1, 'fields' => 'ids', ]; echo '<ul>'; $food_by_year = []; // Current year back to 2000 in decending order. foreach( range( date('Y'), 2000 ) as $year ) { $food_by_year[ $year ] = 0; // December to January in decending order. foreach( range( 12, 1 ) as $month ) { // Skip months in the current year after the current month. $this_months_timestamp = strtotime( $year . '-' . $month ); if ( $this_months_timestamp > time() ) { continue; } $this_months_post_ids = get_posts( array_merge( $base_query_args, [ 'year' => $year, 'monthnum' => $month, ] ) ); $this_food = $this_months_food = 0; if ( ! empty( $this_months_post_ids ) ) { foreach( $this_months_post_ids as $post_id ) { $this_food = (int) get_post_meta( $post_id, 'food', true ); $this_months_food += $this_food; $food_by_year[ $year ] += $this_food; } } printf( '<li>Month %s, Food: %d</li>', date( 'F Y', $this_months_timestamp ), $this_months_food ); } printf( '<li>Year %s, Food: %d</li>', $year, $food_by_year[ $year ] ); } echo '</ul>'; return ob_get_clean(); } );
Wow, that looks perfect, thank you! I have integrated it to my tables design, but is there a way to call the year and monthly value separately so I can style them in their own table rows? I know I can do it by duplicating the shortcode and commenting the year/month printf’s in your code but it looks like a bit of an overkill. I would love to toy with that code and get more field values that way!
Add another
foreach
after the others which iterates over$food_by_year
as$year => $food
.Here:
<?php /** * Plugin Name: Shortcode: Food Journal * Description: Output <code>food</code> counts per month and year with shortcode <code>[food-journal]</code>. * Plugin Author: ??δ??? * Author URI: https://pd.cm/ * Plugin URI: https://www.ads-software.com/support/topic/how-to-best-group-field-values-by-date/ * Version: 2 */ add_shortcode( 'food-journal', function( $atts, $content, $shortcode_tag ){ ob_start(); $base_query_args = [ 'post_type' => 'journal', 'post_status' => 'published', 'posts_per_page' => -1, 'fields' => 'ids', ]; ?> <style> #food-by-month th { background-color: rgba( 10, 50, 10, .8 ); color: #fff; } #food-by-month tr:nth-child(even) { background-color: rgba( 10, 50, 10, .3 ); } #food-by-year th { background-color: rgba( 10, 10, 50, .8 ); color: #fff; } #food-by-year tr:nth-child(even) { background-color: rgba( 10, 10, 50, .3 ); } </style> <table id="food-by-month"><tr><th>Month</th><th>Food</th></tr> <?php $food_by_year = []; // Current year back to 2000 in decending order. foreach( range( date('Y'), 2000 ) as $year ) { $food_by_year[ $year ] = 0; // December to January in decending order. foreach( range( 12, 1 ) as $month ) { // Skip months in the current year after the current month. $this_months_timestamp = strtotime( $year . '-' . $month ); if ( $this_months_timestamp > time() ) { continue; } $this_months_post_ids = get_posts( array_merge( $base_query_args, [ 'year' => $year, 'monthnum' => $month, ] ) ); $this_food = $this_months_food = 0; if ( ! empty( $this_months_post_ids ) ) { foreach( $this_months_post_ids as $post_id ) { $this_food = (int) get_post_meta( $post_id, 'food', true ); $this_months_food += $this_food; $food_by_year[ $year ] += $this_food; } } printf( '<tr><td>%s</td><td>%d</td></tr>', date( 'F Y', $this_months_timestamp ), $this_months_food ); } } ?> </table> <table id="food-by-year"><tr><th>Year</th><th>Food</th></tr> <?php foreach( $food_by_year as $year => $food ) { printf( '<tr><td>%d</td><td>%d</td></tr>', $year, $food ); } echo '</table>'; return ob_get_clean(); } );
Just in case, the same thing, table-based, but with years first:
<?php /** * Plugin Name: Shortcode: Food Journal * Description: Output <code>food</code> counts per month and year with shortcode <code>[food-journal]</code>. * Plugin Author: ??δ??? * Author URI: https://pd.cm/ * Plugin URI: https://www.ads-software.com/support/topic/how-to-best-group-field-values-by-date/ * Version: 3 */ add_shortcode( 'food-journal', function( $atts, $content, $shortcode_tag ){ ob_start(); $base_query_args = [ 'post_type' => 'journal', 'post_status' => 'published', 'posts_per_page' => -1, 'fields' => 'ids', ]; ?> <table id="food-by-month"><tr><th>Month</th><th>Food</th></tr> <?php $food_by_year = []; // Current year back to 2000 in decending order. foreach( range( date('Y'), 2000 ) as $year ) { $food_by_year[ $year ] = 0; // December to January in decending order. foreach( range( 12, 1 ) as $month ) { // Skip months in the current year after the current month. $this_months_timestamp = strtotime( $year . '-' . $month ); if ( $this_months_timestamp > time() ) { continue; } $this_months_post_ids = get_posts( array_merge( $base_query_args, [ 'year' => $year, 'monthnum' => $month, ] ) ); $this_food = $this_months_food = 0; if ( ! empty( $this_months_post_ids ) ) { foreach( $this_months_post_ids as $post_id ) { $this_food = (int) get_post_meta( $post_id, 'food', true ); $this_months_food += $this_food; $food_by_year[ $year ] += $this_food; } } printf( '<tr><td>%s</td><td>%d</td></tr>', date( 'F Y', $this_months_timestamp ), $this_months_food ); } } ?> </table> <?php $food_by_month_html = ob_get_clean(); ob_start(); ?> <style> #food-by-month th { background-color: rgba( 10, 50, 10, .8 ); color: #fff; } #food-by-month tr:nth-child(even) { background-color: rgba( 10, 50, 10, .3 ); } #food-by-year th { background-color: rgba( 10, 10, 50, .8 ); color: #fff; } #food-by-year tr:nth-child(even) { background-color: rgba( 10, 10, 50, .3 ); } </style> <table id="food-by-year"><tr><th>Year</th><th>Food</th></tr> <?php foreach( $food_by_year as $year => $food ) { printf( '<tr><td>%d</td><td>%d</td></tr>', $year, $food ); } echo '</table>'; return ob_get_clean() . $food_by_month_html; } );
For getting more field values, it’s possible to save on SQL queries by calling
get_post_meta( $post_id )
(without the second or third arguments) once for each month. This will return an array of all meta for the post.https://developer.www.ads-software.com/reference/functions/get_post_meta/
And if outputting variables that are strings (rather than integers or formatted dates), one should wrap variables in appropriate escaping functions. e.g.,
esc_html()
,strip_tags()
, etc. It wasn’t necessary in the examples above because all variable datatypes were cast as integers with(int)
or%d
and the only string%s
output was coming fromdate()
.You are next level! Thank you for all the explanations. What if the value entered is 0.5 or 1.5, how do I go about displaying increments?
Use
(float)
instead of(int)
to support decimals; currency field types instead of number field types; format to the number of decimals you want with https://www.php.net/manual/en/function.number-format.phpA little late but thanks again for trying to help me out. It sounded simple but I couldn’t manage to make it work. I changed int to float and also the field type into currency but I don’t understand your third instruction – isn’t that automatically done by POD in the field settings? Here is a screenshot:
https://i.imgur.com/XOZIian.png
And here is the code of the function that I need to have a decimal point on:add_shortcode(
? ? 'ricerolls-journal-years',
? ? function( $atts, $content, $shortcode_tag ){
? ? ? ? ob_start();
? ? ? ? $base_query_args = [
? ? ? ? ? ? 'post_type' => 'daily_journal',
? ? ? ? ? ? 'post_status' => 'published',
? ? ? ? ? ? 'posts_per_page' => -1,
? ? ? ? ? ? 'fields' => 'ids',
? ? ? ? ];
? ? ? ? echo '<tr>';
? ? ? ? $ricerolls_by_year = [];
? ? ? ? // Current year back to 2023 in decending order.
? ? ? ? foreach( range( date('Y'), 2023 ) as $year ) {
? ? ? ? ? ? $ricerolls_by_year[ $year ] = 0;
? ? ? ? ? ? // December to January in decending order.
? ? ? ? ? ? foreach( range( 1, 12 ) as $month ) {
? ? ? ? ? ? ? ? // Skip months in the current year after the current month.
? ? ? ? ? ? ? ? $this_months_timestamp = strtotime( $year . '-' . $month );
? ? ? ? ? ? ? ? if ( $this_months_timestamp > time() ) {
? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? $this_months_post_ids = get_posts(
? ? ? ? ? ? ? ? ? ? array_merge(
? ? ? ? ? ? ? ? ? ? ? ? $base_query_args,
? ? ? ? ? ? ? ? ? ? ? ? [
? ? ? ? ? ? ? ? ? ? ? ? ? ? 'year' => $year,
? ? ? ? ? ? ? ? ? ? ? ? ? ? 'monthnum' => $month,
? ? ? ? ? ? ? ? ? ? ? ? ]
? ? ? ? ? ? ? ? ? ? )
? ? ? ? ? ? ? ? );
? ? ? ? ? ? ? ? $this_ricerolls = $this_months_ricerolls = 0;
? ? ? ? ? ? ? ? if ( ! empty( $this_months_post_ids ) ) {
? ? ? ? ? ? ? ? ? ? foreach( $this_months_post_ids as $post_id ) {
? ? ? ? ? ? ? ? ? ? ? ? $this_ricerolls = (float) get_post_meta( $post_id, 'rice_rolls', true );
? ? ? ? ? ? ? ? ? ? ? ? $this_months_ricerolls += $this_ricerolls;
? ? ? ? ? ? ? ? ? ? ? ? $ricerolls_by_year[ $year ] += $this_ricerolls;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? printf(
? ? ? ? ? ? ? ? '<td colspan="2" bgcolor="#222222" style="white-space:nowrap;border:1px dotted #666666;width:460px;">Year %s</td>
? ? ? ? ? ? ? ? <td id="tdArchive" bgcolor="#000000" style="width:50px;"> %d </td></tr>',
? ? ? ? ? ? ? ? $year,
? ? ? ? ? ? ? ? $ricerolls_by_year[ $year ]
? ? ? ? ? ? );
? ? ? ? }
? ? ? ? echo '
? ? ? ? </tr>';
? ? ? ? return ob_get_clean();
? ? }
);And it’s still rounding up the numbers. I tried floatval () too – same story.
- This reply was modified 1 year, 10 months ago by youknowmenot.
So instead of %d I used %.1f and it worked, but even though I have selected “Remove decimals if ends in zero” in the field settings in POD, it still shows numbers such as 23.0 24.0 instead of just “23” and “24”.
- The topic ‘How to best group field values by date?’ is closed to new replies.