2

I use this to display all posts in all categories.

$args_cat = array(
    // order by category name ascending
    'orderby' => 'name',
    'order' => 'ASC',
    // get only top level categories
    'parent' => 0
);
$categories = get_categories($args_cat);

// Full posts query
// if there are categories filled with posts
if (!empty ($categories) && !is_wp_error( $categories )) {

    foreach ($categories as $category) {

        // Query all posts by slug inside each category
        $args_category_posts = array(
            'post_type' => 'post',
            // The category slug and category name we get from the foreach over all categories
            'category_name' => $category->slug
        );

        $query = new WP_Query($args_category_posts);
        if ($query->have_posts()) {
            while ($query->have_posts()) {
                $query->the_post(); ?>
                <article class="<?php echo $category->slug ?>-article">
                    <h2 class="<?php echo $category->slug ?>-article-title">
                        <a href="<?php echo get_permalink() ?>"><?php echo get_the_title() ?></a>
                    </h2>
                    <p class="<?php echo $category->slug ?>-post-info">
                        <?php the_time('d. m. Y') ?>
                    </p>
                    <div <?php post_class() ?> >
                        <?php the_content(); ?>
                    </div>
                </article> <?php
            }
        } // end loop
    } // end foreach
wp_reset_postdata() ;
} // end if there are categories filled with posts

Having read this, this, this and this I am wondering if I am doing the right thing below.

// Transient API all categories and all posts
$query_categories = get_transient('cached_categories');
if ( false === $query_categories){
    $args_cat = array(
        // order by category name ascending
        'orderby' => 'name',
        'order' => 'ASC',
        // get only top level categories
        'parent' => 0
    );
    // Instead of caching a WP Query I cache 'get_categories($args_cat)', OK to use it like this?
    $query_categories = get_categories($args_cat);
    set_transient('cached_categories', $query_categories, DAY_IN_SECONDS );
}

// Full posts query
// if there are categories filled with posts
if (!empty ($query_categories) && !is_wp_error( $query_categories )) {

    foreach ($query_categories as $category) {

        $query_category_posts = get_transient('cached_posts');
        if ( false === $query_category_posts ){

            // Query all posts by slug inside each category
            $args_category_posts = array(
                'post_type' => 'post',
                // The category slug and category name we get from the foreach over all categories
                'category_name' => $category->slug
            );

            // Here I cache the WP_Query, though this runs for all categories.
            // So am I storing multiple transients here?
            $query_category_posts = new WP_Query($args_category_posts);
            set_transient( 'cached_posts', $query_category_posts, DAY_IN_SECONDS );
        }

        if ($query_category_posts->have_posts()) {
            while ($query_category_posts->have_posts()) {
                $query_category_posts->the_post(); ?>
                <article class="<?php echo $category->slug ?>-article">
                    <h2 class="<?php echo $category->slug ?>-article-title">
                        <a href="<?php echo get_permalink() ?>"><?php echo get_the_title() ?></a>
                    </h2>
                    <p class="<?php echo $category->slug ?>-post-info">
                        <?php the_time('d. m. Y') ?>
                    </p>
                    <div <?php post_class() ?> >
                        <?php the_content(); ?>
                    </div>
                </article> <?php
            }
        } // end loop
    } // end foreach
wp_reset_postdata() ;
} // end if there are categories filled with posts

The output is ok, all categories and posts display fine, though are the categories and posts properly cached now?

In the code I commented the two places where I set the transient but am not sure if this can be used with get_categories() or inside the foreach for all posts in the category.

Edit
The transient works for get_categories(). The var_dump output shows me an array of the WP_Term objects holding the categories available.

$query_categories = get_categories($args_cat);
var_dump($query_categories);
set_transient('cached_categories', $query_categories, DAY_IN_SECONDS );

Edit
The transient inside the foreach is only getting the first category and its posts and repeating that for the number of categories. Other categories than the first are not present, this presumably because the same transient gets called for the amount of number of categories and after the first round of foreach it is full and hence the loop only shows the posts form the first category.

Now how can I make a new transient that holds the posts per category on every cycle of the foreach for the number of categories?

Totally new to the Transient API and trying to make good use of it. Trying to tune the code and keep to best practices, if this is all bad use with errors lurking or no use of all since it does not work please do let me know. A bit of guidance or if I have grasped "the way" to use transients would be highly appreciated.

Solution
Per this answer I had to give the created transient for each category the category name (slug). Otherwise only the first category gets stored in the transient and once it is full for the second cycle it will not be changed. Hence only the posts from the first category were showing.

Only using $category for the transient variable was not working since $category is an object and a string was needed, so I changed it to $category->slug and now it works.

This was done in both places, getting and setting the transient. Thank you!

// Transients API all categories and all posts
$query_categories = get_transient('cached_categories');
if ( false === $query_categories){
    $args_cat = array(
        // order by category name ascending
        'orderby' => 'name',
        'order' => 'ASC',
        // get only top level categories
        'parent' => 0
    );
    // Instead of caching a WP Query I cache 'get_categories()'.
    $query_categories = get_categories($args_cat);
    // var_dump($query_categories);
    set_transient('cached_categories', $query_categories, DAY_IN_SECONDS );
}

// Full posts query
// if there are categories filled with posts
if (!empty ($query_categories) && !is_wp_error( $query_categories )) {

    foreach ($query_categories as $category) {

        // var_dump($category);
        $query_category_posts = get_transient('cached_posts_' . $category->slug );
        if ( false === $query_category_posts ){

            // Query all posts by slug inside each category
            $args_category_posts = array(
                'post_type' => 'post',
                // The category slug and category name we get from the foreach over all categories
                'category_name' => $category->slug
            );

            // Here I cache the WP_Query, though this runs for all categories.
            // Because of that the '$category->slug' is used to serve a string and not an object.
            $query_category_posts = new WP_Query($args_category_posts);         
            set_transient( 'cached_posts_' . $category->slug , $query_category_posts, DAY_IN_SECONDS );
        }

        if ($query_category_posts->have_posts()) {
            while ($query_category_posts->have_posts()) {
                $query_category_posts->the_post(); ?>
                <article class="<?php echo $category->slug ?>-article">
                    <h2 class="<?php echo $category->slug ?>-article-title">
                        <a href="<?php echo get_permalink() ?>"><?php echo get_the_title() ?></a>
                    </h2>
                    <p class="<?php echo $category->slug ?>-post-info">
                        <?php the_time('d. m. Y') ?>
                    </p>
                    <div <?php post_class() ?> >
                        <?php the_content(); ?>
                    </div>
                </article> <?php
            }
        } // end loop
    } // end foreach
wp_reset_postdata() ;
} // end if there are categories filled with posts

1 Answer 1

3

You're saving every query object for each category to the same transient. Because this happens fast and time frame is one day, you're always getting the query object for the first category back. Make your transient name variable with the category, e.g. like this:

$query_category_posts = get_transient('cached_posts_' . $category );

Of course you then need to get the transient with the variable name.

4
  • Thanks heaps for your help! From what I can see it works well. I had to change $category to $category->slug in both places to get the string of the category and not the category object. Now, last thing, is this actually a good thing to do, caching categories and posts? This is for a part of the website where categories and posts only change rarely, if so the cache is deleted and re-created with the changed content. *
    – lowtechsun
    Commented Jun 24, 2017 at 20:44
  • @lowtechsun Ah yes, you're right. That's kind of the reason why I wrote e.g. I didn't went trough all of your question to be honest. WordPress actually has a caching mechanism for queries, but don't ask me for details. In my mind it makes absolutely sense to use transients in your use case. But you can test it yourself, by measuring loading time. Commented Jun 24, 2017 at 21:16
  • Really appreciate the answer, this got me a good step forward, thank you.
    – lowtechsun
    Commented Jun 25, 2017 at 10:36
  • @lowtechsun My pleasure. Commented Jun 25, 2017 at 13:15

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.