Learn icon indicating copy to clipboard operation
Learn copied to clipboard

Lesson: Advanced WordPress Query Techniques

Open thatmitchcanter opened this issue 5 months ago • 7 comments

Details

  • Topic description: Step-by-step tutorial for Advanced WordPress Query Techniques designed for beginners - both in PHP and in block. It includes explanations and code examples
  • Audience (User, Developer, Designer, Contributor, etc.): Developer, Designer
  • Learning objectives (What will the learner be able to do as a result of this content?): The user will be able to:
    • Understand what WP_Query is and how to initiate it with PHP
    • Learn specific examples and querying conditions for WP_Query:
      • Querying by Custom Post Meta
      • Querying by Custom Taxonomies
      • Utilizing Multiple Queries on a Page
      • Combining Multiple Query Conditions
      • Optimizing Queries for Better Performance
  • Content type (Online Workshop, Lesson, Course, or Facilitator Notes): Lesson
  • WordPress version (optional):
  • Will you be creating this content? (Yes or No): Yes* (Some of this lesson is already created; I will be modifying it and adding block based resources to the already written PHP resources)

Related Resources

Next steps for SMEs

Notes

Issue created on Contributor Day during WCUS 2025. This is a conversion of #3051 to the lesson format, along with additional information about the block-based content. Thanks to @sumitsinghwp for the initial writeup.

thatmitchcanter avatar Aug 26 '25 17:08 thatmitchcanter

Reference Notes for Script

A Guide to Using WP_Query in WordPress

The WP_Query class in WordPress provides a flexible way to fetch and display content. In this tutorial, we’ll explore how to:

  • Query posts by specific parameters.
  • Fetch posts using custom fields (meta data).
  • Query posts by custom taxonomies.
  • Create multiple loops on a single page.
  • Optimize queries for performance.

We will also explore the WordPress Query Loop block - a simple way to do simple queries in a Gutenberg-enabled / Full Site Editing enabled theme.

WP_Query in Templates

Basic Usage of WP_Query

The simplest way to use WP_Query is to fetch and display posts. Let’s create a custom query to fetch the latest 5 posts.

Code Example: Place the following code in a template file like page.php or archive.php:

<?php
$args = array(
    'posts_per_page' => 5,
    'post_type'      => 'post',
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
?>
        <h2><?php the_title(); ?></h2>
        <p><?php the_excerpt(); ?></p>
<?php
    endwhile;
else :
    echo 'No posts found.';
endif;

// Reset post data
wp_reset_postdata();
?>

Explanation:

  • posts_per_page: Limits the number of posts to 5.
  • post_type: Specifies the post type to fetch (default is 'post').

Querying Posts by Custom Fields (Meta Data)

Suppose you want to fetch posts where a custom field, rating, has a specific value.

Code Example:

$args = array(
    'post_type'  => 'post',
    'meta_query' => array(
        array(
            'key'     => 'rating',
            'value'   => '5',
            'compare' => '=',
        ),
    ),
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
?>
        <h2><?php the_title(); ?></h2>
        <p>Rating: <?php echo get_post_meta(get_the_ID(), 'rating', true); ?></p>
<?php
    endwhile;
endif;

wp_reset_postdata();

Explanation:

  • meta_query: Filters posts based on custom field (rating).
  • compare: Specifies the comparison operator (e.g., =, >, LIKE).

Querying Posts by Custom Taxonomies

If you have a custom taxonomy (e.g., genre for a "Books" post type), you can query posts by terms.

Code Example:

$args = array(
    'post_type'      => 'book',
    'tax_query'      => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => array('fiction', 'non-fiction'),
        ),
    ),
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
?>
        <h2><?php the_title(); ?></h2>
<?php
    endwhile;
endif;

wp_reset_postdata();

Explanation:

  • tax_query: Filters posts based on taxonomy terms (fiction and non-fiction in the genre taxonomy).
  • field: Specifies whether to use term ID, slug, or name.

Creating Multiple Loops on a Page

You can create multiple queries on the same page, for instance, showing featured and recent posts separately.

Code Example:

// Featured Posts
$featured_args = array(
    'posts_per_page' => 3,
    'meta_query'     => array(
        array(
            'key'   => 'is_featured',
            'value' => '1',
        ),
    ),
);

$featured_query = new WP_Query($featured_args);

if ($featured_query->have_posts()) :
    echo '<h2>Featured Posts</h2>';
    while ($featured_query->have_posts()) : $featured_query->the_post();
?>
        <h3><?php the_title(); ?></h3>
<?php
    endwhile;
endif;

// Reset post data
wp_reset_postdata();

// Recent Posts
$recent_args = array('posts_per_page' => 5);

$recent_query = new WP_Query($recent_args);

if ($recent_query->have_posts()) :
    echo '<h2>Recent Posts</h2>';
    while ($recent_query->have_posts()) : $recent_query->the_post();
?>
        <h3><?php the_title(); ?></h3>
<?php
    endwhile;
endif;

wp_reset_postdata();

Combining Multiple Conditions

To combine multiple filters, you can use meta_query, tax_query, and other parameters together.

Code Example:

$args = array(
    'post_type'  => 'book',
    'meta_query' => array(
        array(
            'key'     => 'rating',
            'value'   => '4',
            'compare' => '>=',
        ),
    ),
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'thriller',
        ),
    ),
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
?>
        <h2><?php the_title(); ?></h2>
<?php
    endwhile;
endif;

wp_reset_postdata();

Optimizing Query Performance

Efficient queries are vital for site performance. Here are some tips:

  • Use cache_results (default is true) to cache query results.
  • Minimize queries by combining conditions in meta_query or tax_query.
  • Use transients to cache frequently queried results.

Example: Caching Query Results with Transients

$cached_posts = get_transient('cached_books_query');

if (!$cached_posts) {
    $args = array('post_type' => 'book', 'posts_per_page' => 10);
    $query = new WP_Query($args);

    $cached_posts = $query->posts;
    set_transient('cached_books_query', $cached_posts, HOUR_IN_SECONDS);
}

foreach ($cached_posts as $post) {
    echo '<h2>' . $post->post_title . '</h2>';
}

The Query Loop Block

For a Gutenberg-enabled / Full Site Editor enabled theme, the Query Loop block allows you to display content in a similar manner, without the need to write PHP code.

The Query Loop block consists of several sections:

  • Query Loop: the parent block used to define the query parameters.
  • Post Template: The content displayed, as well as blocks that control how it's displayed (a list, a grid, etc.)
  • Pagination: Navigational elements to include, such as next/previous buttons or page numbers
  • No Results: A message to display when a query returns no results

Adding the Query Loop block

Click on the block inserter [+] icon and choose the Query Loop block. You can either do this in the Document Overview, or in the content area to open the block inserter pop-up.

You can also use the shortcut /query-loop to quickly insert a Query Loop block.

You will be asked whether you want to choose a pattern, or start blank. If you select the Choose option, you will have various pattern options to choose from.

If you select the Start blank option, you’ll see four variations you can choose from. Select the one you want to use.

Customizing the Appearance of the Query Loop

The Query Loop has several customizations to allow you to create the post loop you need:

  • You can change design elements, such as width, color, and alignment
  • You can add blocks such as the Post Author or Post Excerpt
  • You can rearrange the blocks as you see fit using the movers
  • You can set how many posts by adjusting the Display Settings
  • You can add in blocks like the Featured Image block to bring in further customizations

Customizing the Query Loop

By default, the query loop will inherit the contextual query based on the template it's included on (Archive, Search Results, etc.) You can customize the query by selecting "Custom" in the Settings -> Query Type.

This will open up several options:

  • Post Type: Choose between Posts, Pages, or any Custom Post Types defined
  • Order By: Choose either chronological or alphabetical order, ascending or descending
  • Sticky Posts: Choose to include or exclude, ignore, or only include sticky posts
  • Display: Choose the number of posts to include. You can also offset or set the max pagination on results to further refine your query
  • Filters: Choose any filters to refine the query. Pick specific taxonomies, authors, or any Custom Taxonomies defined

Further Reading

For more information on the Query Loop block and its options, please refer to the WordPress Documentation site.

Note: if you need to customize your WordPress query further, The Advanced Query Loop plugin by Ryan Welcher is a great option. This allows manual post selection, multiple post type inclusion, smart post inclusion/exclusion, and more.

thatmitchcanter avatar Aug 26 '25 18:08 thatmitchcanter

Script

Introduction

In this lesson, we are going to go over how to write WordPress queries with WP_Query. We'll go over how to write both template-based and Query Loop block based queries in WordPress. Finally, we'll learn what conditionals to include, and how to further refine more advanced queries.

Basic Usage of WP_Query

Let’s start with the basics. The simplest use of WP_Query is fetching the latest posts.

$args = array(
    'posts_per_page' => 5,
    'post_type'      => 'post',
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
        the_title('<h2>', '</h2>');
        the_excerpt();
    endwhile;
else :
    echo 'No posts found.';
endif;

wp_reset_postdata();

Here, posts_per_page limits results to five posts, and post_type makes sure we’re only querying regular blog posts.

Always remember to call wp_reset_postdata() after your loop to restore the global post object.

Querying by Custom Fields (Meta Data)

Now let’s query posts based on custom fields. For example, posts with a custom field called rating equal to 5.

$args = array(
    'post_type'  => 'post',
    'meta_query' => array(
        array(
            'key'     => 'rating',
            'value'   => '5',
            'compare' => '=',
        ),
    ),
);

The meta_query parameter lets you filter by custom fields, and compare allows operators like equals, greater than, or even LIKE.

Querying Custom Taxonomies

What if you’re working with a custom taxonomy, like a genre for books?

$args = array(
    'post_type' => 'book',
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => array('fiction', 'non-fiction'),
        ),
    ),
);

This example pulls in books that belong to the “fiction” or “non-fiction” genres. The field parameter can use a term’s ID, slug, or name.

Multiple Loops on the Same Page

You’re not limited to a single loop. You can create multiple queries on one page — for example, featured posts and recent posts.

// Featured Posts
$featured_args = array(
    'posts_per_page' => 3,
    'meta_query'     => array(
        array(
            'key'   => 'is_featured',
            'value' => '1',
        ),
    ),
);

$featured_query = new WP_Query($featured_args);

if ($featured_query->have_posts()) :
    echo '<h2>Featured Posts</h2>';
    while ($featured_query->have_posts()) : $featured_query->the_post();
?>
        <h3><?php the_title(); ?></h3>
<?php
    endwhile;
endif;

// Reset post data
wp_reset_postdata();

// Recent Posts
$recent_args = array('posts_per_page' => 5);

$recent_query = new WP_Query($recent_args);

if ($recent_query->have_posts()) :
    echo '<h2>Recent Posts</h2>';
    while ($recent_query->have_posts()) : $recent_query->the_post();
?>
        <h3><?php the_title(); ?></h3>
<?php
    endwhile;
endif;

wp_reset_postdata();

Here, the first loop fetches three featured posts using a custom field, while the second shows the five most recent posts.

Combining Multiple Conditions

You can also combine multiple filters — like custom fields and taxonomies.

$args = array(
    'post_type'  => 'book',
    'meta_query' => array(
        array(
            'key'     => 'rating',
            'value'   => '4',
            'compare' => '>=',
        ),
    ),
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'thriller',
        ),
    ),
);

$query = new WP_Query($args);

if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
?>
        <h2><?php the_title(); ?></h2>
<?php
    endwhile;
endif;

wp_reset_postdata();

Optimizing for Performance

Efficient queries are critical for site speed. Some best practices include:

  • Using cache_results wisely,
  • Minimizing the number of queries by combining conditions,
  • And caching queries with transients.
$cached_posts = get_transient('cached_books_query');

if (!$cached_posts) {
    $args = array('post_type' => 'book', 'posts_per_page' => 10);
    $query = new WP_Query($args);

    $cached_posts = $query->posts;
    set_transient('cached_books_query', $cached_posts, HOUR_IN_SECONDS);
}

foreach ($cached_posts as $post) {
    echo '<h2>' . $post->post_title . '</h2>';
}

Here, we cache the results of a book query for one hour using set_transient(). This reduces database load significantly.

The Query Loop Block (No Code Needed)

If you’re using a Gutenberg-enabled or Full Site Editing theme, you can achieve many of these results with the Query Loop block — no PHP required.

The Query Loop block has four key parts:

  • The Query Loop itself,
  • The Post Template,
  • Pagination controls,
  • And a No Results block.

You can insert it using the block inserter or simply typing /query-loop.

From there, you can customize the layout, add blocks like Featured Image or Post Excerpt, and adjust display settings like post type, sorting, and filters.

To recap, WP_Query gives you fine-grained control when coding custom templates, while the Query Loop block provides an easy, no-code alternative inside the block editor.

For deeper customization, check out the WordPress documentation or plugins like Advanced Query Loop by Ryan Welcher.

thatmitchcanter avatar Aug 26 '25 18:08 thatmitchcanter

General question for the WP Community: how would a screen reader verbalize a code block like this (as an example):

5, 'post_type' => 'post', ); $query = new WP_Query($args); if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post(); ?>
    <h2><?php the_title(); ?></h2>
    <p><?php the_excerpt(); ?></p>

Is that even a concern? Would the WCAG flag learn.wordpress.org if this kind of coding sample couldn't be articulated in a way that makes the context clear, ie. "this is a sample of PHP code?"

frereenfant-cmd avatar Aug 26 '25 22:08 frereenfant-cmd

@thatmitchcanter

Great job! We can discuss it further here in the issue, but I just wanted to say that this is awesome.

rjekic avatar Aug 27 '25 07:08 rjekic

@frereenfant-cmd

Could you clarify this further and share more details about the exact use case? Are you asking about a general scenario where the code you mentioned is used on a WordPress site, or do you mean specifically how a screen reader would interpret the code inside a code block? For instance, how would a screen reader handle the content on a GitHub page like this?

Thank you.

rjekic avatar Aug 27 '25 07:08 rjekic

Hi Rade,

The scenario I had in mind would be something like this: I have Mitch's training article open in my browser, but due to macular degeneration I can't read the text. I activate screen reader accessibility, and the voiceover plays.

My question is, "How will the screen reader handle the content and interpret the PHP code block that Mitch included in the article?" Will I hear something like this:

Left angle bracket, question mark, php, line break, dollar sign, args equals array, left parenthesis, line break, posts_per_page, arrow symbol, post, line break...

Again, this is a general scenario not specifically tied to Mitch's script. I'm trying to understand more about screen reader accessibility and how it would read back a code block.

Thanks, Tom D.


Tom Dumenjich 310-844-5319 @.***

On Wed, Aug 27, 2025 at 12:55 AM Rade Jekić @.***> wrote:

rjekic left a comment (WordPress/Learn#3273) https://github.com/WordPress/Learn/issues/3273#issuecomment-3227184821

@frereenfant-cmd https://github.com/frereenfant-cmd

Could you clarify this further and share more details about the exact use case? Are you asking about a general scenario where the code you mentioned is used on a WordPress site, or do you mean specifically how a screen reader would interpret the code inside a code block? For instance, how would a screen reader handle the content on a GitHub page like this?

Thank you.

— Reply to this email directly, view it on GitHub https://github.com/WordPress/Learn/issues/3273#issuecomment-3227184821, or unsubscribe https://github.com/notifications/unsubscribe-auth/BWRMWU6BGYZTMUN4IWYNVED3PVP7FAVCNFSM6AAAAACE3WLGY6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTEMRXGE4DIOBSGE . You are receiving this because you were mentioned.Message ID: @.***>

frereenfant-cmd avatar Aug 27 '25 18:08 frereenfant-cmd

@frereenfant-cmd

Thank you for the detailed explanation. I created a new issue, so we can discuss this further in that issue -> https://github.com/WordPress/Learn/issues/3283

rjekic avatar Sep 04 '25 09:09 rjekic