File "query.php"
Full path: /home/dora/public_html/wp-content/themes/bricks/includes/query.php
File size: 35.17 KB
MIME-type: --
Charset: utf-8
<?php
namespace Bricks;
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class Query {
/**
* The query unique ID
*/
private $id = '';
/**
* Element ID
*/
public $element_id = '';
/**
* Element settings
*/
public $settings = [];
/**
* Query vars
*/
public $query_vars = [];
/**
* Type of object queried: 'post', 'term', 'user'
*/
public $object_type = 'post';
/**
* Query result (WP_Posts | WP_Term_Query | WP_User_Query | Other)
*/
public $query_result;
/**
* Query results total
*/
public $count = 0;
/**
* Query results total pages
*/
public $max_num_pages = 1;
/**
* Is looping
*
* @var boolean
*/
public $is_looping = false;
/**
* When looping, keep the iteration index
*/
public $loop_index = 0;
/**
* When looping, keep the object
*/
public $loop_object = null;
/**
* Store the original post before looping to restore the context (nested loops)
*/
private $original_post_id = 0;
/**
* Cache key
*/
private $cache_key = false;
/**
* Class constructor
*
* @param array $element
*/
public function __construct( $element = [] ) {
$this->register_query();
$this->element_id = ! empty( $element['id'] ) ? $element['id'] : '';
$this->object_type = ! empty( $element['settings']['query']['objectType'] ) ? $element['settings']['query']['objectType'] : 'post';
// Remove object type from query vars to avoid future conflicts
unset( $element['settings']['query']['objectType'] );
$this->settings = ! empty( $element['settings'] ) ? $element['settings'] : [];
// STEP: Set the query vars from the element settings (@since 1.8)
$this->query_vars = self::prepare_query_vars_from_settings( $this->settings );
// STEP: Perform the query, set the query result, count and max_num_pages (@since 1.8)
$this->run();
}
/**
* Add this query to the global store
*/
public function register_query() {
global $bricks_loop_query;
$this->id = Helpers::generate_random_id( false );
if ( ! is_array( $bricks_loop_query ) ) {
$bricks_loop_query = [];
}
$bricks_loop_query[ $this->id ] = $this;
}
/**
* Calling unset( $query ) does not destroy query quickly enough
*
* Have to call the 'destroy' method explicitly before unset.
*/
public function __destruct() {
$this->destroy();
}
/**
* Use the destroy method to remove the query from the global store
*
* @return void
*/
public function destroy() {
global $bricks_loop_query;
unset( $bricks_loop_query[ $this->id ] );
}
/**
* Get the query cache
*
* @since 1.5
*
* @return mixed
*/
public function get_query_cache() {
if ( ! isset( Database::$global_settings['cacheQueryLoops'] ) || ! bricks_is_frontend() || bricks_is_builder_call() ) {
return false;
}
// Check: Nesting query?
$parent_query_id = self::is_any_looping();
$parent_object_id = $parent_query_id ? self::get_loop_object_id( $parent_query_id ) : 0;
// Include in the cache key a representation of the query vars to break cache for certain scenarios like pagination or search keywords
$query_vars = json_encode( $this->query_vars );
// Get & set query loop cache (@since 1.5)
$this->cache_key = md5( "brx_query_{$this->element_id}_{$query_vars}_{$parent_object_id}" );
return wp_cache_get( $this->cache_key, 'bricks' );
}
/**
* Set the query cache
*
* @since 1.5
*
* @return void
*/
public function set_query_cache( $object ) {
if ( ! $this->cache_key ) {
return;
}
wp_cache_set( $this->cache_key, $object, 'bricks', MINUTE_IN_SECONDS );
}
/**
* Prepare query_vars for the Query before running it
* Remove unwanted keys, set defaults, populate correct query vars, etc.
* Static method to be used by other classes. (Bricks\Database)
*
* @since 1.8
*/
public static function prepare_query_vars_from_settings( $settings ) {
$query_vars = isset( $settings['query'] ) ? $settings['query'] : [];
// Unset infinite scroll
if ( isset( $query_vars['infinite_scroll'] ) ) {
unset( $query_vars['infinite_scroll'] );
}
// Do not use meta_key if orderby is not set to meta_value or meta_value_num (@since 1.8)
if ( isset( $query_vars['meta_key'] ) ) {
$orderby = isset( $query_vars['orderby'] ) ? $query_vars['orderby'] : '';
if ( ! in_array( $orderby, [ 'meta_value', 'meta_value_num' ] ) ) {
unset( $query_vars['meta_key'] );
}
}
$object_type = self::get_query_object_type();
$element_id = self::get_query_element_id();
// Meta Query vars
$query_vars = self::parse_meta_query_vars( $query_vars );
// Set different query vars depending on the object type
switch ( $object_type ) {
case 'post':
// Attachments
$is_attachment_query = false;
// post_type can be 'string' or 'array'
$post_type = ! empty( $query_vars['post_type'] ) ? $query_vars['post_type'] : false;
if ( $post_type ) {
if ( is_array( $post_type ) ) {
$is_attachment_query = in_array( 'attachment', $post_type ) && count( $post_type ) == 1;
} else {
$is_attachment_query = $post_type === 'attachment';
}
}
// @since 1.5: If the post type is 'attachment', change default post status to 'inherit' @see: https://developer.wordpress.org/reference/classes/wp_query/#post-type-parameters
$query_vars['post_status'] = $is_attachment_query ? 'inherit' : 'publish';
// post_mime_type: used only for post_type = 'attachment' (@since 1.5)
if ( $is_attachment_query ) {
$mime_types = isset( $query_vars['post_mime_type'] ) ? bricks_render_dynamic_data( $query_vars['post_mime_type'] ) : 'image';
$mime_types = explode( ',', $mime_types );
$tquery_vars['post_mime_type'] = $mime_types;
}
// Page & Pagination
// @since 1.7.1 - Standardize use the get_paged_query_var() function to get the paged value
$query_vars['paged'] = self::get_paged_query_var( $query_vars );
// Value must be -1 or > 1 (0 is not allowed)
$query_vars['posts_per_page'] = ! empty( $query_vars['posts_per_page'] ) ? intval( $query_vars['posts_per_page'] ) : get_option( 'posts_per_page' );
// Exclude current post
if ( isset( $query_vars['exclude_current_post'] ) ) {
// @since 1.8 - Capture exclude_current_post value inside builder call (#861m48kv4)
if ( is_single() || is_page() || bricks_is_builder_call() ) {
$query_vars['post__not_in'][] = get_the_ID();
}
unset( $query_vars['exclude_current_post'] );
}
// @since 1.5 - Post parent
if ( isset( $query_vars['post_parent'] ) ) {
$post_parent = bricks_render_dynamic_data( $query_vars['post_parent'] );
if ( strpos( $post_parent, ',' ) !== false ) {
$post_parent = explode( ',', $post_parent );
// @since 1.7.1
$query_vars['post_parent__in'] = (array) $post_parent;
unset( $query_vars['post_parent'] );
} else {
$query_vars['post_parent'] = (int) $post_parent;
}
}
// Tax query
$query_vars = self::set_tax_query_vars( $query_vars );
// @see: https://academy.bricksbuilder.io/article/filter-bricks-posts-merge_query/
$merge_query = apply_filters( 'bricks/posts/merge_query', true, $element_id );
/**
* Merge wp_query vars and posts element query vars
*
* @since 1.7: Merge query only if 'disable_query_merge' control is not set!
*/
if ( $merge_query && ( is_archive() || is_author() || is_search() || is_home() ) && empty( $query_vars['disable_query_merge'] ) ) {
global $wp_query;
$query_vars = wp_parse_args( $query_vars, $wp_query->query );
}
/**
* REST API /load_query_page adds "_merge_vars" to the query to make sure the archive context is maintained on infinite scroll
*
* @since 1.5.1
*/
if ( ! empty( $query_vars['_merge_vars'] ) ) {
$merge_query_vars = $query_vars['_merge_vars'];
unset( $query_vars['_merge_vars'] );
$query_vars = wp_parse_args( $query_vars, $merge_query_vars );
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-posts-query_vars/
// @since 1.3.6 Added $element_id
$query_vars = apply_filters( 'bricks/posts/query_vars', $query_vars, $settings, $element_id );
break;
case 'term':
// Number. Default is "0" (all) but as a safety procedure we limit the number
$query_vars['number'] = isset( $query_vars['number'] ) ? $query_vars['number'] : get_option( 'posts_per_page' );
$paged = self::get_paged_query_var( $query_vars );
// Pagination: Fix the offset value (@since 1.5)
$offset = ! empty( $query_vars['offset'] ) ? $query_vars['offset'] : 0;
// If pagination exists, and number is limited (!= 0), use $offset as the pagination trigger
if ( $paged !== 1 && ! empty( $query_vars['number'] ) ) {
$query_vars['offset'] = ( $paged - 1 ) * $query_vars['number'] + $offset;
}
// Hide empty
if ( isset( $query_vars['show_empty'] ) ) {
$query_vars['hide_empty'] = false;
unset( $query_vars['show_empty'] );
}
if ( isset( $query_vars['child_of'] ) ) {
$query_vars['child_of'] = bricks_render_dynamic_data( $query_vars['child_of'] );
}
if ( isset( $query_vars['parent'] ) ) {
$query_vars['parent'] = bricks_render_dynamic_data( $query_vars['parent'] );
}
// Include & Exclude terms
if ( isset( $query_vars['tax_query'] ) ) {
$query_vars['include'] = self::convert_terms_to_ids( $query_vars['tax_query'] );
unset( $query_vars['tax_query'] );
}
if ( isset( $query_vars['tax_query_not'] ) ) {
$query_vars['exclude'] = self::convert_terms_to_ids( $query_vars['tax_query_not'] );
unset( $query_vars['tax_query_not'] );
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-terms-query_vars/
$query_vars = apply_filters( 'bricks/terms/query_vars', $query_vars, $settings, $element_id );
break;
case 'user':
// Unset post_type
if ( isset( $query_vars['post_type'] ) ) {
unset( $query_vars['post_type'] );
}
// Paged
$query_vars['paged'] = self::get_paged_query_var( $query_vars );
// Pagination (number, offset, paged). Default is "-1" but as a safety procedure we limit the number (0 is not allowed)
$query_vars['number'] = ! empty( $query_vars['number'] ) ? $query_vars['number'] : get_option( 'posts_per_page' );
// Pagination: Fix the offset value (@since 1.5)
$offset = ! empty( $query_vars['offset'] ) ? $query_vars['offset'] : 0;
if ( ! empty( $offset ) && $query_vars['paged'] !== 1 ) {
$query_vars['offset'] = ( $query_vars['paged'] - 1 ) * $query_vars['number'] + $offset;
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-users-query_vars/
$query_vars = apply_filters( 'bricks/users/query_vars', $query_vars, $settings, $element_id );
break;
}
return $query_vars;
}
/**
* Perform the query (maybe cache)
*
* Set $this->query_result, $this->count, $this->max_num_pages
*
* @return void (@since 1.8)
*/
public function run() {
$count = $this->count;
$max_num_pages = $this->max_num_pages;
$query_vars = $this->query_vars;
switch ( $this->object_type ) {
case 'post':
$result = $this->run_wp_query();
// STEP: Populate the total count
$count = empty( $query_vars['no_found_rows'] ) ? $result->found_posts : ( is_array( $result->posts ) ? count( $result->posts ) : 0 );
$max_num_pages = empty( $query_vars['posts_per_page'] ) ? 1 : ceil( $count / $query_vars['posts_per_page'] );
break;
case 'term':
$result = $this->run_wp_term_query();
// Pagination: Fix the offset value (@since 1.5)
$offset = ! empty( $query_vars['offset'] ) ? $query_vars['offset'] : 0;
// STEP: Populate the total count
if ( empty( $query_vars['number'] ) ) {
$count = ! empty( $result ) && is_array( $result ) ? count( $result ) : 0;
} else {
$args = $query_vars;
unset( $args['offset'] );
unset( $args['number'] );
// Numeric string containing the number of terms in that taxonomy or WP_Error if the taxonomy does not exist.
$count = wp_count_terms( $args );
if ( is_wp_error( $count ) ) {
$count = 0;
} else {
$count = (int) $count;
$count = $offset <= $count ? $count - $offset : 0;
}
}
// STEP : Populate the max number of pages
$max_num_pages = empty( $query_vars['number'] ) ? 1 : ceil( $count / $query_vars['number'] );
break;
case 'user':
$users_query = $this->run_wp_user_query();
// STEP: The query result
$result = $users_query->get_results();
// STEP: Populate the total count of the users in this query
$count = $users_query->get_total();
// Pagination: Fix the offset value (@since 1.5)
$offset = ! empty( $query_vars['offset'] ) ? $query_vars['offset'] : 0;
// Subtract the $offset to fix pagination
$count = $offset <= $count ? $count - $offset : 0;
// STEP : Populate the max number of pages
$max_num_pages = empty( $query_vars['number'] ) ? 1 : ceil( $count / $query_vars['number'] );
break;
default:
// Allow other query providers to return a query result (Woo Cart, ACF, Metabox...)
$result = apply_filters( 'bricks/query/run', [], $this );
$count = ! empty( $result ) && is_array( $result ) ? count( $result ) : 0;
break;
}
/**
* Set the query result, count and max_num_pages in a centralized way
* Previously this was done in run_wp_query(), run_wp_term_query() and run_wp_user_query()
* Filters provided
*
* @see https://academy.bricksbuilder.io/article/filter-bricks-query-result/
* @see https://academy.bricksbuilder.io/article/filter-bricks-query-result_count/
*
* @since 1.8
*/
$this->query_result = apply_filters( 'bricks/query/result', $result, $this );
$this->count = apply_filters( 'bricks/query/result_count', $count, $this );
// $this->max_num_pages = apply_filters( 'bricks/query/result_max_num_pages', $max_num_pages, $this );
$this->max_num_pages = $max_num_pages; // This value seems like not in use as max_num_pages should be coming from
}
/**
* Run WP_Term_Query
*
* @see https://developer.wordpress.org/reference/classes/wp_term_query/
*
* @return array Terms (WP_Term)
*/
public function run_wp_term_query() {
// Cache?
$result = $this->get_query_cache();
if ( $result === false ) {
$terms_query = new \WP_Term_Query( $this->query_vars );
$result = $terms_query->get_terms();
$this->set_query_cache( $result );
}
return $result;
}
/**
* Run WP_User_Query
*
* @see https://developer.wordpress.org/reference/classes/wp_user_query/
*
* @return WP_User_Query (@since 1.8)
*/
public function run_wp_user_query() {
// Cache?
$users_query = $this->get_query_cache();
if ( $users_query === false ) {
$users_query = new \WP_User_Query( $this->query_vars );
$this->set_query_cache( $users_query );
}
return $users_query;
}
/**
* Run WP_Query
*
* @return object
*/
public function run_wp_query() {
// Cache?
$posts_query = $this->get_query_cache();
if ( $posts_query === false ) {
add_action( 'pre_get_posts', [ $this, 'set_pagination_with_offset' ], 5 );
add_filter( 'found_posts', [ $this, 'fix_found_posts_with_offset' ], 5, 2 );
/**
* Use random seed when: 'orderby' is 'rand' && 'randomSeedTtl' > 0
*
* Default: 60 minutes
*/
$use_random_seed = isset( $this->query_vars['orderby'] ) && $this->query_vars['orderby'] === 'rand' && ! ( isset( $this->settings['query']['randomSeedTtl'] ) && absint( $this->settings['query']['randomSeedTtl'] ) === 0 );
// @since 1.7.1 - Avoid duplicate posts when using 'rand' orderby
if ( $use_random_seed ) {
add_filter( 'posts_orderby', [ $this, 'set_bricks_query_loop_random_order_seed' ], 11 );
}
$posts_query = new \WP_Query( $this->query_vars );
// @since 1.7.1 - Avoid duplicate posts when using 'rand' orderby
if ( $use_random_seed ) {
remove_filter( 'posts_orderby', [ $this, 'set_bricks_query_loop_random_order_seed' ], 11 );
}
remove_action( 'pre_get_posts', [ $this, 'set_pagination_with_offset' ], 5 );
remove_filter( 'found_posts', [ $this, 'fix_found_posts_with_offset' ], 5, 2 );
$this->set_query_cache( $posts_query );
}
return $posts_query;
}
/**
* Get the page number for a query based on the query var "paged"
*
* @since 1.5
*
* @return integer
*/
public static function get_paged_query_var( $query_vars ) {
// @since 1.7.1 - Avoid query_var param merged accidentally if disable_query_merge is true
$disable_query_merge = isset( $query_vars['disable_query_merge'] );
if ( $disable_query_merge ) {
// @since 1.7.1 - Return paged 1 if disable_query_merge is true
return 1;
}
$paged = 1;
if ( get_query_var( 'page' ) ) {
// Check for 'page' on static front page
$paged = get_query_var( 'page' );
} elseif ( get_query_var( 'paged' ) ) {
$paged = get_query_var( 'paged' );
} else {
$paged = ! empty( $query_vars['paged'] ) ? abs( $query_vars['paged'] ) : 1;
}
return intval( $paged );
}
/**
* Parse the Meta Query vars through the DD logic
*
* @Since 1.5
*
* @param array $query_vars
* @return array
*/
public static function parse_meta_query_vars( $query_vars ) {
if ( empty( $query_vars['meta_query'] ) ) {
return $query_vars;
}
foreach ( $query_vars['meta_query'] as $key => $query_item ) {
unset( $query_vars['meta_query'][ $key ]['id'] );
if ( empty( $query_vars['meta_query'][ $key ]['value'] ) ) {
continue;
}
$query_vars['meta_query'][ $key ]['value'] = bricks_render_dynamic_data( $query_vars['meta_query'][ $key ]['value'] );
}
if ( ! empty( $query_vars['meta_query_relation'] ) ) {
$query_vars['meta_query']['relation'] = $query_vars['meta_query_relation'];
}
unset( $query_vars['meta_query_relation'] );
return $query_vars;
}
/**
* Set 'tax_query' vars (e.g. Carousel, Posts, Related Posts)
*
* Include & exclude terms of different taxonomies
*
* @since 1.3.2
*/
public static function set_tax_query_vars( $query_vars ) {
// Include terms
if ( isset( $query_vars['tax_query'] ) ) {
$terms = $query_vars['tax_query'];
$tax_query = [];
foreach ( $terms as $term ) {
if ( ! is_string( $term ) ) {
continue;
}
$term_parts = explode( '::', $term );
$taxonomy = isset( $term_parts[0] ) ? $term_parts[0] : false;
$term = isset( $term_parts[1] ) ? $term_parts[1] : false;
if ( ! $taxonomy || ! $term ) {
continue;
}
if ( isset( $tax_query[ $taxonomy ] ) ) {
$tax_query[ $taxonomy ]['terms'][] = $term;
} else {
$tax_query[ $taxonomy ] = [
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => [ $term ],
];
}
}
$tax_query = array_values( $tax_query );
if ( count( $tax_query ) > 1 ) {
$tax_query['relation'] = 'OR';
$query_vars['tax_query'] = [ $tax_query ];
} else {
$query_vars['tax_query'] = $tax_query;
}
}
// Exclude terms
if ( isset( $query_vars['tax_query_not'] ) ) {
$terms = $query_vars['tax_query_not'];
$tax_query_exclude = [];
foreach ( $query_vars['tax_query_not'] as $term ) {
if ( ! is_string( $term ) ) {
continue;
}
$term_parts = explode( '::', $term );
$taxonomy = $term_parts[0];
$term = $term_parts[1];
if ( isset( $tax_query_exclude[ $taxonomy ] ) ) {
$tax_query_exclude[ $taxonomy ]['terms'][] = $term;
} else {
$tax_query_exclude[ $taxonomy ] = [
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => [ $term ],
'operator' => 'NOT IN',
];
}
}
$tax_query_exclude = array_values( $tax_query_exclude );
if ( count( $tax_query_exclude ) > 1 ) {
$tax_query_exclude['relation'] = 'AND';
$query_vars['tax_query'][] = [ $tax_query_exclude ];
} else {
$query_vars['tax_query'][] = $tax_query_exclude;
}
unset( $query_vars['tax_query_not'] );
}
if ( isset( $query_vars['tax_query_advanced'] ) ) {
foreach ( $query_vars['tax_query_advanced'] as $tax_query ) {
if ( empty( $tax_query['terms'] ) ) {
continue;
}
$tax_query['terms'] = bricks_render_dynamic_data( $tax_query['terms'] );
if ( strpos( $tax_query['terms'], ',' ) ) {
$tax_query['terms'] = explode( ',', $tax_query['terms'] );
$tax_query['terms'] = array_map( 'trim', $tax_query['terms'] );
}
unset( $tax_query['id'] );
if ( isset( $tax_query['include_children'] ) ) {
$tax_query['include_children'] = filter_var( $tax_query['include_children'], FILTER_VALIDATE_BOOLEAN );
}
$query_vars['tax_query'][] = $tax_query;
}
}
if ( isset( $query_vars['tax_query'] ) && is_array( $query_vars['tax_query'] ) && count( $query_vars['tax_query'] ) > 1 ) {
$query_vars['tax_query']['relation'] = isset( $query_vars['tax_query_relation'] ) ? $query_vars['tax_query_relation'] : 'AND';
}
unset( $query_vars['tax_query_relation'] );
unset( $query_vars['tax_query_advanced'] );
return $query_vars;
}
/**
* Modifies $query offset variable to make pagination work in combination with offset.
*
* @see https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
* Note that the link recommends exiting the filter if $query->is_paged returns false,
* but then max_num_pages on the first page is incorrect.
*
* @param \WP_Query $query WordPress query.
*/
public function set_pagination_with_offset( $query ) {
if ( ! isset( $this->query_vars['offset'] ) ) {
return;
}
$new_offset = $this->query_vars['offset'] + ( $query->get( 'paged', 1 ) - 1 ) * $query->get( 'posts_per_page' );
$query->set( 'offset', $new_offset );
}
/**
* By default, WordPress includes offset posts into the final post count.
* This method excludes them.
*
* @see https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
* Note that the link recommends exiting the filter if $query->is_paged returns false,
* but then max_num_pages on the first page is incorrect.
*
* @param int $found_posts Found posts.
* @param \WP_Query $query WordPress query.
* @return int Modified found posts.
*/
public function fix_found_posts_with_offset( $found_posts, $query ) {
if ( ! isset( $this->query_vars['offset'] ) ) {
return $found_posts;
}
return $found_posts - $this->query_vars['offset'];
}
/**
* Set the initial loop index (needed for the infinite scroll)
*
* @since 1.5
*
* @return void
*/
public function init_loop_index() {
if ( $this->object_type == 'post' ) {
$offset = isset( $this->query_vars['offset'] ) ? $this->query_vars['offset'] : 0;
return $offset + ( $this->query_vars['posts_per_page'] > 0 ? ( $this->query_vars['paged'] - 1 ) * $this->query_vars['posts_per_page'] : 0 );
} elseif ( $this->object_type == 'term' ) {
return isset( $this->query_vars['offset'] ) ? $this->query_vars['offset'] : 0;
} elseif ( $this->object_type == 'user' ) {
$offset = isset( $this->query_vars['offset'] ) ? $this->query_vars['offset'] : 0;
$page = isset( $this->query_vars['paged'] ) ? $this->query_vars['paged'] : 1;
return $offset + ( $this->query_vars['number'] > 0 ? ( $page - 1 ) * $this->query_vars['number'] : 0 );
}
return 0;
}
/**
* Main render function
*
* @param string $callback to render each item
* @param array $args callback function args
* @param boolean $return_array whether returns a string or an array of all the iterations
* @return mixed
*/
public function render( $callback, $args, $return_array = false ) {
// Remove array keys
$args = array_values( $args );
// Query results
$query_result = $this->query_result;
$content = [];
$this->loop_index = $this->init_loop_index();
$this->is_looping = true;
// @see https://academy.bricksbuilder.io/article/action-bricks-query-before_loop (@since 1.7.2)
do_action( 'bricks/query/before_loop', $this, $args );
// Query is empty
if ( empty( $this->count ) ) {
$content[] = $this->get_no_results_content();
}
// Iterate
else {
// STEP: Loop posts
if ( $this->object_type == 'post' ) {
$this->original_post_id = get_the_ID();
while ( $query_result->have_posts() ) {
$query_result->the_post();
$this->loop_object = get_post();
$part = call_user_func_array( $callback, $args );
$content[] = self::parse_dynamic_data( $part, get_the_ID() );
$this->loop_index++;
}
}
// STEP: Loop terms
elseif ( $this->object_type == 'term' ) {
foreach ( $query_result as $term_object ) {
$this->loop_object = $term_object;
$part = call_user_func_array( $callback, $args );
$content[] = self::parse_dynamic_data( $part, get_the_ID() );
$this->loop_index++;
}
}
// STEP: Loop users
elseif ( $this->object_type == 'user' ) {
foreach ( $query_result as $user_object ) {
$this->loop_object = $user_object;
$part = call_user_func_array( $callback, $args );
$content[] = self::parse_dynamic_data( $part, get_the_ID() );
$this->loop_index++;
}
}
// STEP: Other render providers (wooCart, ACF repeater, Meta Box groups)
else {
$this->original_post_id = get_the_ID();
foreach ( $query_result as $loop_key => $loop_object ) {
// @see: https://academy.bricksbuilder.io/article/filter-bricks-query-loop_object/
$this->loop_object = apply_filters( 'bricks/query/loop_object', $loop_object, $loop_key, $this );
$part = call_user_func_array( $callback, $args );
$content[] = self::parse_dynamic_data( $part, get_the_ID() );
$this->loop_index++;
}
}
}
// @see https://academy.bricksbuilder.io/article/action-bricks-query-after_loop (@since 1.7.2)
do_action( 'bricks/query/after_loop', $this, $args );
$this->loop_object = null;
$this->is_looping = false;
$this->reset_postdata();
return $return_array ? $content : implode( '', $content );
}
public static function parse_dynamic_data( $content, $post_id ) {
if ( is_array( $content ) ) {
if ( isset( $content['background']['image']['useDynamicData'] ) ) {
$size = isset( $content['background']['image']['size'] ) ? $content['background']['image']['size'] : BRICKS_DEFAULT_IMAGE_SIZE;
$images = Integrations\Dynamic_Data\Providers::render_tag( $content['background']['image']['useDynamicData'], $post_id, 'image', [ 'size' => $size ] );
if ( isset( $images[0] ) ) {
$content['background']['image']['url'] = is_numeric( $images[0] ) ? wp_get_attachment_image_url( $images[0], $size ) : $images[0];
unset( $content['background']['image']['useDynamicData'] );
}
}
return map_deep( $content, [ 'Bricks\Integrations\Dynamic_Data\Providers', 'render_content' ] );
} else {
return bricks_render_dynamic_data( $content, $post_id );
}
}
/**
* Reset the global $post to the parent query or the global $wp_query
*
* @since 1.5
*
* @return void
*/
public function reset_postdata() {
// Reset is not needed
if ( empty( $this->original_post_id ) ) {
return;
}
$looping_query_id = self::is_any_looping();
// Not a nested query, reset global query
if ( ! $looping_query_id ) {
wp_reset_postdata();
}
// Set the parent query context
global $post;
$post = get_post( $this->original_post_id );
setup_postdata( $post );
}
/**
* Get the current Query object
*
* @return Query
*/
public static function get_query_object( $query_id = false ) {
global $bricks_loop_query;
if ( ! is_array( $bricks_loop_query ) || $query_id && ! array_key_exists( $query_id, $bricks_loop_query ) ) {
return false;
}
return $query_id ? $bricks_loop_query[ $query_id ] : end( $bricks_loop_query );
}
/**
* Get the current Query object type
*
* @return string
*/
public static function get_query_object_type( $query_id = '' ) {
$query = self::get_query_object( $query_id );
return $query ? $query->object_type : '';
}
/**
* Get the object of the current loop iteration
*
* @return mixed
*/
public static function get_loop_object( $query_id = '' ) {
$query = self::get_query_object( $query_id );
return $query ? $query->loop_object : null;
}
/**
* Get the object ID of the current loop iteration
*
* @return mixed
*/
public static function get_loop_object_id( $query_id = '' ) {
$object = self::get_loop_object( $query_id );
$object_id = 0;
if ( is_a( $object, 'WP_Post' ) ) {
$object_id = $object->ID;
}
if ( is_a( $object, 'WP_Term' ) ) {
$object_id = $object->term_id;
}
if ( is_a( $object, 'WP_User' ) ) {
$object_id = $object->ID;
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-query-loop_object_id/
return apply_filters( 'bricks/query/loop_object_id', $object_id, $object, $query_id );
}
/**
* Get the object type of the current loop iteration
*
* @return mixed
*/
public static function get_loop_object_type( $query_id = '' ) {
$object = self::get_loop_object( $query_id );
$object_type = null;
if ( is_a( $object, 'WP_Post' ) ) {
$object_type = 'post';
}
if ( is_a( $object, 'WP_Term' ) ) {
$object_type = 'term';
}
if ( is_a( $object, 'WP_User' ) ) {
$object_type = 'user';
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-query-loop_object_type/
return apply_filters( 'bricks/query/loop_object_type', $object_type, $object, $query_id );
}
/**
* Get the current loop iteration index
*
* @return mixed
*/
public static function get_loop_index() {
$query = self::get_query_object();
return $query && $query->is_looping ? $query->loop_index : '';
}
/**
* Check if the render function is looping (in the current query)
*
* @param string $element_id if specificed checks if the element_id matches the element that is set to loop (e.g. container)
* @return boolean
*/
public static function is_looping( $element_id = '', $query_id = '' ) {
$query = self::get_query_object( $query_id );
if ( ! $query ) {
return false;
}
if ( empty( $element_id ) ) {
return $query->is_looping;
}
// Still here, search for the element_id query
$query = self::get_query_for_element_id( $element_id );
return $query ? $query->is_looping : false;
}
/**
* Get query object created for a specific element ID
*
* @param string $element_id
* @return mixed
*/
public static function get_query_for_element_id( $element_id = '' ) {
if ( empty( $element_id ) ) {
return false;
}
global $bricks_loop_query;
if ( empty( $bricks_loop_query ) ) {
return false;
}
foreach ( $bricks_loop_query as $key => $query ) {
if ( $query->element_id == $element_id ) {
return $query;
}
}
return false;
}
/**
* Get element ID of query loop element
*
* @param object $query Defaults to current query.
*
* @since 1.4
*
* @return string|boolean Element ID or false
*/
public static function get_query_element_id( $query = '' ) {
$query = self::get_query_object( $query );
return ! empty( $query->element_id ) ? $query->element_id : false;
}
/**
* Check if there is any active query looping (nested queries) and if yes, return the query ID of the most deep query
*
* @return mixed
*/
public static function is_any_looping() {
global $bricks_loop_query;
if ( empty( $bricks_loop_query ) ) {
return false;
}
$query_ids = array_reverse( array_keys( $bricks_loop_query ) );
foreach ( $query_ids as $query_id ) {
if ( $bricks_loop_query[ $query_id ]->is_looping ) {
return $query_id;
}
}
return false;
}
/**
* Convert a list of option strings taxonomy::term_id into a list of term_ids
*/
public static function convert_terms_to_ids( $terms = [] ) {
if ( empty( $terms ) ) {
return [];
}
$options = [];
foreach ( $terms as $term ) {
if ( ! is_string( $term ) ) {
continue;
}
$term_parts = explode( '::', $term );
// $taxonomy = $term_parts[0];
$options[] = $term_parts[1];
}
return $options;
}
public function get_no_results_content() {
// Return: Avoid showing no results message when infinite scroll is enabled (@since 1.5.6)
if ( bricks_is_rest_call() ) {
return '';
}
$content = isset( $this->settings['query']['no_results_text'] ) ? $this->settings['query']['no_results_text'] : '';
if ( ! empty( $content ) ) {
$content = '<div class="bricks-posts-nothing-found"><p>' . $content . '</p></div>';
$content = bricks_render_dynamic_data( $content );
$content = do_shortcode( $content );
}
// @see: https://academy.bricksbuilder.io/article/filter-bricks-query_no_results_content/
$content = apply_filters( 'bricks/query/no_results_content', $content, $this->settings, $this->element_id );
return $content;
}
/**
* Use random seed to make sure the order is the same for all queries of the same element
*
* The transient is also deleted when the random seed setting inside the query loop control is changed.
*
* @param string $order_statement
* @return string
* @since 1.7.1
*/
public function set_bricks_query_loop_random_order_seed( $order_statement ) {
// Transient name is based on the element ID
$transient_name = "bricks_query_loop_random_seed_{$this->element_id}";
$random_seed = get_transient( $transient_name );
if ( ! $random_seed ) {
// Generate a random seed for this query
$random_seed = rand( 0, 99999 );
// Default transient TTL is 60 minutes
$random_seed_ttl = ! empty( $this->settings['query']['randomSeedTtl'] ) ? absint( $this->settings['query']['randomSeedTtl'] ) : 60;
set_transient( $transient_name, $random_seed, $random_seed_ttl * MINUTE_IN_SECONDS );
}
$order_statement = 'RAND(' . $random_seed . ')';
return $order_statement;
}
/**
* All query arguments that can be set for the archive query
* https://developer.wordpress.org/reference/classes/wp_query/#parameters
*
* @return array
*
* @since 1.8
*/
public static function archive_query_arguments() {
$arguments = [
'post_type',
'post_status',
'p',
'page_id',
'name',
'pagename',
'page',
'hour',
'minute',
'second',
'year',
'monthnum',
'day',
'w',
'm',
'cat',
'category_name',
'category__and',
'category__in',
'category__not_in',
'tag',
'tag_id',
'tag__and',
'tag__in',
'tag__not_in',
'tag_slug__and',
'tag_slug__in',
'taxonomy',
'term',
'field',
'operator',
'include_children',
'paged',
'posts_per_page',
'nopaging',
'offset',
'ignore_sticky_posts',
'post_parent',
'post_parent__in',
'post_parent__not_in',
'post__in',
'post__not_in',
'post_name__in',
'author',
'author_name',
'author__in',
'author__not_in',
's',
'exact',
'sentence',
'meta_key',
'meta_value',
'meta_value_num',
'meta_compare',
'meta_query',
'date_query',
'cache_results',
'update_post_term_cache',
'update_post_meta_cache',
'no_found_rows',
'order',
'orderby',
'perm',
'post_mime_type',
'comment_count',
'comment_status',
'post_comment_status',
];
// NOTE: Undocumented
return apply_filters( 'bricks/query/archive_query_arguments', $arguments );
}
/**
* All bricks query object types that can be set for the archive query.
* If there is custom query by user and it might be used as archive query, should be added here.
*
* @return array
*
* @since 1.8
*/
public static function archive_query_supported_object_types() {
$object_types = [
'post',
'term',
'user',
];
// NOTE: Undocumented
return apply_filters( 'bricks/query/archive_query_supported_object_types', $object_types );
}
}