$args Normalized args. * @param int $page Requested page. * @return array */ public static function build( array $args, $page = 1 ) { $page = max( 1, (int) $page ); $per_page = (int) $args['posts_per_page']; $offset = (int) $args['offset']; $paginated = 'none' !== $args['pagination']; $query_args = array( 'post_type' => $args['post_type'], 'post_status' => 'publish', 'posts_per_page' => $per_page, 'orderby' => $args['orderby'], 'order' => $args['order'], 'ignore_sticky_posts' => (bool) $args['ignore_sticky'], // Skip the extra COUNT query when there is nothing to paginate. 'no_found_rows' => ! $paginated, ); if ( $offset > 0 ) { // `offset` and `paged` are mutually exclusive in WP_Query. $query_args['offset'] = $offset + ( ( $page - 1 ) * $per_page ); } else { $query_args['paged'] = $page; } if ( ! empty( $args['exclude'] ) ) { $query_args['post__not_in'] = $args['exclude']; } if ( ! empty( $args['author'] ) ) { $query_args['author__in'] = $args['author']; } $tax_query = self::tax_query( $args['tax'] ); if ( ! empty( $tax_query ) ) { // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Term filtering is a documented shortcode feature and only runs when the author asked for it. $query_args['tax_query'] = $tax_query; } /** * Filters the query arguments before the grid is queried. * * @since 0.1 * * @param array $query_args WP_Query arguments. * @param array $args Normalized grid args. * @param int $page Requested page. */ return apply_filters( 'mmpg_query_args', $query_args, $args, $page ); } /** * Runs the grid query. * * @since 0.1 * * @param array $args Normalized args. * @param int $page Requested page. * @return \WP_Query */ public static function query( array $args, $page = 1 ) { $query = new \WP_Query( self::build( $args, $page ) ); if ( ! empty( $args['show_image'] ) && ! empty( $query->posts ) ) { // One meta query for every thumbnail instead of one per card. update_post_thumbnail_cache( $query ); } return $query; } /** * Returns the number of pages available for a grid. * * @since 0.1 * * @param \WP_Query $query Executed query. * @param array $args Normalized args. * @return int */ public static function max_pages( \WP_Query $query, array $args ) { $offset = (int) $args['offset']; if ( $offset < 1 ) { return (int) $query->max_num_pages; } // `found_posts` ignores the offset, so the tail has to be removed by hand. $per_page = max( 1, (int) $args['posts_per_page'] ); $available = max( 0, (int) $query->found_posts - $offset ); return (int) ceil( $available / $per_page ); } /** * Builds the `tax_query` from normalized clauses. * * @since 0.1 * * @param array> $clauses Normalized clauses. * @return array */ private static function tax_query( array $clauses ) { $tax_query = array(); foreach ( $clauses as $clause ) { $ids = array(); $slugs = array(); foreach ( $clause['terms'] as $term ) { if ( is_int( $term ) || ctype_digit( (string) $term ) ) { $ids[] = (int) $term; } else { $slugs[] = $term; } } $parts = array(); if ( ! empty( $ids ) ) { $parts[] = array( 'taxonomy' => $clause['taxonomy'], 'field' => 'term_id', 'terms' => $ids, ); } if ( ! empty( $slugs ) ) { $parts[] = array( 'taxonomy' => $clause['taxonomy'], 'field' => 'slug', 'terms' => $slugs, ); } if ( count( $parts ) > 1 ) { $parts['relation'] = 'OR'; $tax_query[] = $parts; } elseif ( ! empty( $parts ) ) { $tax_query[] = $parts[0]; } } if ( count( $tax_query ) > 1 ) { $tax_query['relation'] = 'AND'; } return $tax_query; } }