In most of the previous tutorials covering nested query loops in Bricks on this site, we accessed the parent query’s looping object in the inner query loop using one of the two methods shared in Parent Query Loop Object – Traversing Nested Query Loops Up in Bricks. Typically we will have a custom criterion for the outer query and the inner would be the posts query.
There’s a slightly better way of going about the nested query loops and this Pro tutorial shows how a custom query type can be created for the inner query loop (in addition to the one for outer QL) that accesses the parent query’s loop object in the code outside the Bricks editor. This makes it simpler to implement the nested queries in the Bricks editor wherein we just have to select the custom query type rather than having to specify the outer query loop-enabled element’s Bricks ID.
The steps can be outlined like this:
- Register custom query types for outer and inner loops using
bricks/setup/control_optionsfilter. - Specify the names of function handlers for both the query types using
bricks/query/runfilter. - Define the function handler for the outer QL. If you want to pass a single value to the inner QL, let this return a scalar value like a string/integer/float. If you want pass multiple values to the inner QL, let this return an array.
- Define a function that either returns a string or an array via
BricksQuery::get_loop_object( $looping_query_id ). - In the inner QL’s function handler, access the above function and return an array of post objects.
- In Bricks editor set the outer QL and inner QL query types to your custom ones.
- Use
bl_get_loop_object_stringorbl_get_loop_object_arrayto output the label or heading in the outer QL and standard Bricks dynamic data tags like post_title (wrapped in curly braces) in the inner QL.
Example 1
Method 2 of Weekday Column Posts Bricks Query Loop tutorial.
Example 2
Here’s how Posts Grouped by Month and Year in Bricks tutorial can be re-done using this approach.
<?php
// Add new query type controls
add_filter( 'bricks/setup/control_options', 'bl_add_custom_query_types_20241021' );
/**
* Add custom query types to Bricks control options.
*
* @param array $control_options The existing control options.
* @return array Modified control options.
*/
function bl_add_custom_query_types_20241021( array $control_options ): array {
$control_options['queryTypes']['bl_post_months'] = esc_html__( 'Post Months', 'bricks' );
$control_options['queryTypes']['bl_post_months_posts'] = esc_html__( 'Post Months Posts', 'bricks' );
return $control_options;
}
// Handle custom query types
add_filter( 'bricks/query/run', 'bl_run_custom_queries_20241021', 10, 2 );
/**
* Run custom queries.
*
* @param array $results The existing query results.
* @param BricksQuery $query_obj The query object.
* @return array Modified query results.
*/
function bl_run_custom_queries_20241021( array $results, BricksQuery $query_obj ): array {
switch ( $query_obj->object_type ) {
case 'bl_post_months':
return bl_get_post_months();
case 'bl_post_months_posts':
return bl_get_post_months_posts( $query_obj );
default:
return $results;
}
}
/**
* Get array of post months.
*
* @return array Array of post months.
*/
function bl_get_post_months(): array {
// Access the global WordPress database object
global $wpdb;
// Query the database to get distinct year and month combinations for published posts
$months = $wpdb->get_results(
$wpdb->prepare(
"SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
FROM $wpdb->posts
WHERE post_type = %s AND post_status = %s
ORDER BY post_date DESC",
'post', // your post type here
'publish'
)
);
// Initialize an array to store formatted month data
$formatted_months = [];
// Loop through each month returned by the database query
foreach ( $months as $month ) {
// Create a DateTime object for the first day of the month
$date = new DateTime( "{$month->year}-{$month->month}-01" );
// Add formatted month data to the array
$formatted_months[] = [
'label' => $date->format( 'F Y' ), // Format as "Month Year" (e.g., "January 2024")
'year' => $month->year, // Store the year
'month' => $month->month, // Store the month number
];
}
// Return the array of formatted month data
return $formatted_months;
}
/**
* Get posts for a given post month.
*
* @param BricksQuery $query_obj The query object.
* @return array Array of post objects.
*/
function bl_get_post_months_posts( BricksQuery $query_obj ): array {
$post_month = bl_get_loop_object_array();
if ( empty( $post_month ) || ! isset( $post_month['year'] ) || ! isset( $post_month['month'] ) ) {
return ['post__in' => [0]];
}
$args = [
'post_type' => 'post', // your post type here
'posts_per_page' => -1, // Get all posts. Limit to a specific number - as small as possible.
'date_query' => [
[
'year' => $post_month['year'],
'month' => $post_month['month'],
],
],
'orderby' => 'date',
'order' => 'DESC',
];
// return get_posts( $args );
$posts = get_posts( $args );
return is_wp_error( $posts ) ? ['post__in' => [0]] : $posts;
}
if ( ! function_exists( 'bl_get_loop_object_array' ) ) {
/**
* Get the current loop object array.
*
* @return array The current loop object or an empty array.
*/
function bl_get_loop_object_array(): array {
$looping_query_id = BricksQuery::is_any_looping();
if ( $looping_query_id ) {
$loop_object = BricksQuery::get_loop_object( $looping_query_id );
return $loop_object;
}
return [];
}
}
add_filter( 'bricks/code/echo_function_names', function() {
return [
'bl_get_loop_object_array',
];
} );
For the ‘Month Year’ heading (Ex.: March 2023):
{echo:bl_get_loop_object_array:array_value|label}