Are you a GenerateCustomer?

Do you have an active GP Premium or GenerateBlocks Pro license key?

Create a GenerateSupport account for GeneratePress and GenerateBlocks support in one dedicated place.

Create an account
Already have a GenerateSupport account? Login

Just browsing?

Feel free to browse the forums. Support for our free versions is provided on WordPress.org (GeneratePress, GenerateBlocks).

Want to become a premium user? Learn more below.

GenerateBlocks Query Loop – Random post that stays fixed for 24 hours

  • Hi,

    I’m using GeneratePress + GenerateBlocks and I’m trying to create a “Post of the Day” section on my homepage.

    Goal:

    • Display 1 random post
    • The selected post should remain the same for 24 hours (from midnight to midnight)
    • It should change automatically the next day
    • It must not re-randomize on every page refresh

    Currently:

    • I’m using a GenerateBlocks Query Loop
    • Post type: Post
    • Posts per page: 1
    • Order by: Random

    The issue is that it re-randomizes on every page load, which is expected behavior for orderby=rand, but not what I need.

    Cheers!

  • Hi,

    This requires custom PHP since the Query Loop’sorderby=rand will always re-randomize on each page load.

    The solution uses a transient to cache the randomly selected post ID until midnight, so it stays fixed for the day:

    add_filter( 'generateblocks_query_loop_args', function( $query_args, $attributes ) {
        
        $classes = $attributes['className'] ?? '';
        
        if ( strpos( $classes, 'post-of-the-day' ) === false ) {
            return $query_args;
        }
        
        $transient_key = 'post_of_the_day_id';
        $post_id = get_transient( $transient_key );
        
        if ( false === $post_id ) {
            $random_post = get_posts( array(
                'post_type'      => $query_args['post_type'] ?? 'post',
                'posts_per_page' => 1,
                'orderby'        => 'rand',
                'fields'         => 'ids',
            ) );
            
            if ( ! empty( $random_post ) ) {
                $post_id = $random_post[0];
                
                // Cache until midnight (based on Settings → General → Timezone)
                $now = current_time( 'timestamp' );
                $midnight = strtotime( 'tomorrow midnight', $now );
                set_transient( $transient_key, $post_id, $midnight - $now );
            }
        }
        
        if ( $post_id ) {
            $query_args['post__in'] = array( $post_id );
            $query_args['orderby'] = 'post__in';
        }
        
        return $query_args;
    }, 99, 2 );

    Setup:

    1. Add the code to your child theme’s functions.php or a code snippets plugin
    2. On your Query Loop block, add the CSS class post-of-the-day (Settings -> Additional CSS Class(es))
    3. Set Posts Per Page to 1
    4. Remove any orderby=rand setting from the Query Loop itself

    The random selection happens once per day at midnight, and that post is then shown to all visitors until the next midnight. Note that “midnight” is based on the timezone set in Settings -> General -> Timezone.

    Note on caching: If you’re using page caching (LiteSpeed, WP Rocket, Cloudflare, etc.), the cached page may continue showing the old post even after midnight until the cache clears. You may need to either:

    • Set your page cache TTL to expire around midnight
    • Exclude this page from page caching
    • Set up a scheduled cache purge at midnight

    The transient itself will work correctly — it’s just that visitors may see a cached version of the page until it refreshes.

  • Hi,

    Thanks a lot George, the transient solution works perfectly!

    I have a follow-up question:

    Is it possible to limit the “Post of the Day” selection to a specific subset of posts, and still keep the same daily-random behavior?

    For example:

    • Only include posts from a specific category (e.g. “Daily Prayer”)
    • Or only include posts with a specific tag
    • Or even manually include specific post IDs

    Then, among those filtered posts, select 1 random post and cache it until midnight — exactly like the current solution.

    Would it be enough to modify the get_posts() arguments inside the transient logic (for example by adding category_name, tag, or post__in)?

    Just want to confirm the cleanest way to restrict the pool without breaking the daily caching behavior.

    Thanks.

  • Hi,

    You just need to modify the get_posts() arguments inside the filter. The caching behavior stays the same.

    Here are examples for each scenario:

    Filter by category:

    $random_post = get_posts( array(
        'post_type'      => 'post',
        'posts_per_page' => 1,
        'orderby'        => 'rand',
        'category_name'  => 'daily-prayer', // Use the category slug
        'fields'         => 'ids',
    ) );

    Filter by tag:

    $random_post = get_posts( array(
        'post_type'      => 'post',
        'posts_per_page' => 1,
        'orderby'        => 'rand',
        'tag'            => 'featured', // Use the tag slug
        'fields'         => 'ids',
    ) );

    Filter by specific post IDs:

    $allowed_posts = array( 123, 456, 789 ); // Your chosen post IDs
    
    $random_post = get_posts( array(
        'post_type'      => 'post',
        'posts_per_page' => 1,
        'orderby'        => 'rand',
        'post__in'       => $allowed_posts,
        'fields'         => 'ids',
    ) );

    Combine multiple filters:

    $random_post = get_posts( array(
        'post_type'      => 'post',
        'posts_per_page' => 1,
        'orderby'        => 'rand',
        'category_name'  => 'daily-prayer',
        'tag'            => 'featured',
        'fields'         => 'ids',
    ) );

    The transient caching logic remains unchanged — it will just select from your filtered pool instead of all posts.

  • Thanks George, you’re awesome!

  • You are welcome!

Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.