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 3 years, 1 month ago by
youknowmenot. Reason: Fixed my 2nd problem
-
This topic was modified 3 years, 1 month ago by
-
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.2000can 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://wordpress.org/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
foreachafter the others which iterates over$food_by_yearas$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://wordpress.org/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://wordpress.org/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.wordpress.org/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%dand the only string%soutput 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 3 years 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”.
-
This reply was modified 3 years ago by
The topic ‘How to best group field values by date?’ is closed to new replies.