x Number of Random Bricks Query Loop Items(s) to be Shown Every x Hours

A user asks:

Query a metabox group, but limit items?

…i cannot see how to pick a random text field in that group and change it every X minutes. is it doable?

i need to show only one item in the metabox group, at random, and randomly pick another after one hour.

btw, dunno why the groups have no options like posts or pages.

Non standard query types like ACF Repeaters, Meta Box Groups and Relationships do not have the same controls that the post query loops have.

This Pro tutorial shows how PHP transients and bricks/query/result filter can be used to specify how many random items should be output in the query loop’s result set and for how long.

After implementing the tutorial, you could, for example, show one random row of a Meta Box group on page load, continue to show the same for an hour (the same for all visitors/users), and then output another random row (other than what was shown in the last hour) for the second hour and so on.

Before:

After:

After another hour:

Step 1

In a Bricks template or Page, set up your query loop.

In this example, we are querying a Meta Box group.

Step 2

Before we add the code snippet let’s first understand what the original query result and the query object looks like.

Original query result:

This can be seen by adding this snippet in a code snippets plugin like WPCodeBox or in the child theme’s functions.php at the end w/o the opening PHP tag:

<?php 

add_filter( 'bricks/query/result', function( $result, $query_obj ) {
	if ( $query_obj->element_id !== 'cwndii' ) {
		return $result;
	}

	print( "<pre>" . print_r( $result, true ) . "</pre>" );

	return $result;
}, 30, 2 );

where cwndii is the Bricks ID of the query loop enabled element.

The result is going to be different depending on what is being queried.

Query object:

This can be seen via:

<?php 

add_filter( 'bricks/query/result', function( $result, $query_obj ) {
	if ( $query_obj->element_id !== 'cwndii' ) {
		return $result;
	}

	print( "<pre>" . print_r( $query_obj, true ) . "</pre>" );

	return $result;
}, 30, 2 );

Coming to the task at hand, add the following in child theme‘s functions.php (w/o the opening PHP tag) or a code snippets plugin:

<?php

/**
 * Filter to modify query results for a specific Bricks element.
 * 
 * @param array $result    The original query result.
 * @param object $query_obj The query object.
 * @return array           The modified query result.
 */
add_filter( 'bricks/query/result', function( $result, $query_obj ) : array {
    // Check if this is the specific element we want to modify
    if ( $query_obj->element_id !== 'cwndii' ) {
        return $result;
    }

    // Set the cache duration (in hours)
    $cache_duration_hours = 1; // Change this value to adjust the cache duration

    // Set the number of items to return
    $num_items = 1; // Change this value to adjust the number of items returned

    return bl_get_random_cached_items( $result, $query_obj->element_id, $num_items, $cache_duration_hours );
}, 30, 2 );

/**
 * Get random items from the array and cache them for a specified duration.
 * 
 * @param array  $items               The array of items to choose from.
 * @param string $element_id          The ID of the Bricks element.
 * @param int    $num_items           The number of items to return.
 * @param int    $cache_duration_hours The duration to cache the items, in hours.
 * @return array                      An array containing the selected items.
 */
function bl_get_random_cached_items( array $items, string $element_id, int $num_items = 1, int $cache_duration_hours = 1 ) : array {
    // Convert hours to seconds
    $cache_duration = $cache_duration_hours * HOUR_IN_SECONDS;

    // Create unique transient keys for this element
    $transient_key = 'bl_random_items_' . $element_id;
    $previous_items_key = 'bl_previous_items_' . $element_id;

    // Try to get the cached items
    $cached_items = get_transient( $transient_key );

    // If cached items exist and the count matches the requested number, return them
    if ( $cached_items !== false && count( $cached_items ) === $num_items ) {
        return $cached_items;
    }

    // Get the previously shown items
    $previous_items = get_transient( $previous_items_key ) ?: [];

    // Filter out the previous items to avoid showing them again
    $available_items = array_diff_key( $items, array_flip( $previous_items ) );

    // If filtering removed too many items, reset to the full list
    if ( count( $available_items ) < $num_items ) {
        $available_items = $items;
    }

    // Select random items
    $random_items = [];
    $keys = array_keys( $available_items );
    for ( $i = 0; $i < $num_items; $i++ ) {
        if ( empty( $keys ) ) break;
        $random_index = array_rand( $keys );
        $random_items[] = $available_items[ $keys[ $random_index ] ];
        unset( $keys[ $random_index ] );
    }

    // Cache the selected items
    set_transient( $transient_key, $random_items, $cache_duration );

    // Store these items as the "previous" items for twice the cache duration
    set_transient( $previous_items_key, array_keys( $random_items ), $cache_duration * 2 );

    // Return the selected items
    return $random_items;
}

Adjust the $cache_duration_hours value in the filter function to set your desired cache duration.

Set the $num_items value to specify how many random items you want to return.

Ensure the $query_obj->element_id matches the ID of your Bricks element.

That’s it! Check the front end.