Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Custom queries: WP_Query

The main Loop prints what WordPress selected from the page address. But the front page also needs the three latest books, and an article needs related posts below it. The WP_Query class serves such selections.

A second loop

<?php
// front-page.php
 
$books = new WP_Query([
    'post_type' => 'book',
    'posts_per_page' => 3,
]);
 
if ($books->have_posts()) :
    while ($books->have_posts()) :
        $books->the_post();
        ?>
        <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
        <?php
    endwhile;
 
    wp_reset_postdata();
endif;

The have_posts() and the_post() methods are called on the query object. After the loop wp_reset_postdata() restores the post that was current before it.

Common parameters

ParameterWhat it sets
post_typeThe post type: post, page, a custom type
posts_per_pageHow many posts; -1 means all
orderby, orderOrdering: date, title, rand, meta_value_num; ASC or DESC
category_nameA category slug
sA search phrase
post__in, post__not_inOnly these identifiers, or all except them
pagedThe page number
authorThe author's identifier

Selecting by taxonomy

$query = new WP_Query([
    'post_type' => 'book',
    'tax_query' => [
        [
            'taxonomy' => 'genre',
            'field' => 'slug',
            'terms' => ['fantasy', 'detective'],
        ],
    ],
]);

Selecting by a field

$query = new WP_Query([
    'post_type' => 'book',
    'meta_key' => 'book_price',
    'orderby' => 'meta_value_num',
    'order' => 'ASC',
    'meta_query' => [
        [
            'key' => 'book_price',
            'value' => 20,
            'compare' => '<=',
            'type' => 'NUMERIC',
        ],
    ],
]);

The query returns books costing 20 or less, ordered by price.

<?php
// single.php
 
$related = new WP_Query([
    'category__in' => wp_get_post_categories(get_the_ID()),
    'post__not_in' => [get_the_ID()],
    'posts_per_page' => 3,
]);

Just an array of posts

When no loop is needed, get_posts() is handier — it returns an array of objects:

$latest = get_posts(['post_type' => 'book', 'numberposts' => 5]);
 
foreach ($latest as $book) {
    echo esc_html($book->post_title);
}

Changing the main query

To change what the main Loop prints, you do not create a new query but adjust the existing one — with the pre_get_posts hook:

// functions.php
 
add_action('pre_get_posts', function (WP_Query $query) {
    if (is_admin() || !$query->is_main_query()) {
        return;
    }
 
    if ($query->is_post_type_archive('book')) {
        $query->set('posts_per_page', 12);
        $query->set('orderby', 'title');
        $query->set('order', 'ASC');
    }
});

Pagination keeps working correctly.

Common mistake

Without wp_reset_postdata() after your own loop, the_title() and get_the_ID() further down the template refer to the last post of your query: someone else's comments and title appear under the article. And do not use the old query_posts() function: it breaks pagination — pre_get_posts exists for the main query.

Thanks for reading!

Donate to the site — we work hard for you.