0

I have a blog that is documenting an airplane build process. Posts describe the work done each day, and are organized into a category for each component. Each post has a custom field for hours logged during the tasks completed.

I would like to show the total hours (across all categories) as well as the total hours for each category not just on each post but on the landing page as well. For example:

Wings: 5 hrs <-- custom field for hours

Wings total: 84 hrs <-- sum of custom fields for hours in the Wings category

Build total: 432 hrs <-- sum of all custom fields for hours

I am struggling with how to approach this cleanly. I can create a custom post type, however this does not solve the problem of showing these totals elsewhere. Also, I would prefer not to do a query on each post and perform the operation on each page load as there will eventually be hundreds of posts (although that is probably possible).

1 Answer 1

1

We have various options for achieving this, but I want you to start with the categories since WordPress does not allow a post without a category, which means we won't miss a post from the following query.

Let's say, you have created a meta using the custom fields to save an accumulated number for each post. (Alternatively: Could use ACF or any other plugin features).

enter image description here

We can access the number of a post with the following inbuilt WP function:

get_post_meta($post_id, 'hours', true);

Since you don't want to make it call on every page load, you could create a cron job and call it on a timely basis, and save the totals. I think saving the number in the options table is fine for this purpose:

function accumulation_save() {

    $total = 0;
    $options = array();
    
    // Get all the categories
    $cats = get_categories();

    // Run through the each categories
    foreach ($cats as $key => $cat) {
        $subtotal = 0;

        // Grab posts from a category
        $the_query = new WP_Query(array('cat' => $cat->term_id));
        if ( $the_query->have_posts() ) {
            while ( $the_query->have_posts() ) {
                $the_query->the_post();
                $subtotal += (int)get_post_meta(get_the_ID(), 'hours', true);
                $options[get_the_title()] = get_post_meta(get_the_ID(), 'hours', true);
            }
        }
        $total += $subtotal;
        $options[$cat->name] = $subtotal;
        wp_reset_postdata();
    }
    $options["Build total"] = $total;

    update_option('accumulation', $options);
}

Here you can find a detailed explanation of how to create the cron job.

And we have to print it as:

function accumulation_print() {

    $options = get_option('accumulation');

    foreach ($options as $key => $value) {
        echo "$key : $value hrs<br>";
    }
}

PS: If you are assigned a post in multiple categories, the totals will add up.

2
  • This is a great help, thank you. One issue I am currently having is the loop appears to count the hours in some posts twice even though they are only in one category. One thought is to build an array of post ids and not count it if it already appears in the list, but this seems inefficient.
    – John
    Commented Oct 20, 2023 at 0:11
  • Glad to hear that. This code is just an example and you could manipulate it in your need. Array keys are the best way to eliminate duplication in PHP ($options[get_the_title()]). So, you could use the most unique identifier for the keys like get_the_ID ($options[get_the_ID()]...). And I highly encourage you to print all the post names, hours and compare them. Also don't forget to exclude the unpublished posts (draft, scheduled, pending, ...)
    – Baljka Gan
    Commented Oct 20, 2023 at 2:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.