Let’s say you want to do something unique with the way posts are queried and displayed on a particular page of your website. Maybe you want to have multiple queries — one for featured posts and one for recent posts. Or perhaps you want to exclude certain categories of posts from your blog page.

Whatever your goal, you decide to build a custom page template with a query that does something a bit differently. However, before you start coding you have a decision to make: which WordPress query tool should you use?

WordPress includes several different queries: WP_query, query_posts(), get_posts(), get_pages(), and pre_get_posts. In many cases, you could use more than one of these tools to achieve the desired results. However, the question remains, which on should you use?

In this post, we’ll look at each of these five WordPress query functions in detail. We’ll learn how each one works, identify any inherent limitations or pitfalls, and determine the scenarios in which each should be used. By the end of this post, you’ll know be able to make an educated decision as to the proper tool for your post querying needs.

Click on a link below to explore a specific item or keep reading to learn about all of these options:

Let’s get right to it.

WP_Query

WP_Query is the class behind (almost) every WordPress query. When you load a page or post in WordPress, a WP_query object, $query, is created and pulls up the relevant post or page data. Think of WP_Query as the engine that powers most WordPress queries.

You use WP_Query even without realizing you’re using it. When your load a URL the WordPress core builds a database query with WP_Query based on the URL and the settings you’ve applied to your website. So, if you access a page with a URL like http://example.com/category/wordpress WordPress creates a WP_Query object that locates all posts in the WordPress category and loads all of the post data.

WP_Query powers the standard post and page queries built into WordPress and it can also be used to build custom queries. This is done with a bit of object-oriented programming. All you have to do is create a new variable and declare it as a new instance of the WP_Query class, like this:

Of course, then you need to do something with the results of the query. However, that something that’s beyond the scope of this tutorial. Instead, refer to the list of tutorials at the end of this post if you need help putting WP_Query into practice.

As a WordPress developer, you’ll probably use WP_Query more frequently that any other query function or hook. It’s versatile and powerful. While some of the other queries covered in this post may save you a few keystrokes in some cases, in general, you can’t go wrong selecting WP_Query for your custom query writing needs.

The only exception to this is the case where all you need to do is filter the results of the standard query. In that case, pre_get_posts is the tool you should use. So let’s take a look at it next.

pre_get_posts

pre_get_posts is a hook, not a function. Rather than query the database anew, pre_get_posts allows you to modify the $query object before the database is queried — effectively filtering the results returned by the standard query.

In most cases, pre_get_posts is paired with conditional tags to filter the query results in specific situations. For example, you could use pre_get_posts to return a different number of posts on the site homepage. In essence, if you want to run the standard query but modify it in some way, pre_get_posts is the tool for the job.

There are some instances in which pre_get_posts will not work and should not be used. The WordPress Codex suggests two such instances:

  • The pre_get_posts filter should not be used to alter the query on the template for a single page because doing so will interfere with properties already set by parse_query().
  • The pre_get_posts filter will not work if added to a template files such as archive.php because these files are loaded after the main query has already run.

Where does that leave us? That means that pre_get_posts is a great choice for modifying the query loading posts into the main loop of the homepage, blog page, and individual pages such as page.php and single.php.

However, sometimes filtering the standard query isn’t enough. Perhaps you want to use multiple WordPress queries, or manipulate query results in a fashion that pre_get_posts won’t allow. In that case, you can head back to WP_Query or read on for a few additional options.

query_posts()

If you dig around looking for WordPress tutorials from several years ago, you’ll find many tutorials recommending the use of query_posts(). However, modern tutorials almost universally recommend against this. Here’s why.

The query_posts() function replaces the main query object, $query, which is created and used by the default loop run by the WordPress core. It does this by creating a new WP_Query instance and assigning it to the $query object.

That may make it sound as if query_posts() is really powerful and useful. However, fiddling with the core loop means that query_posts() has major downsides and should be avoided.

The official WordPress code reference provides several reasons why use of query_posts() should be avoided in the vast majority of cases. The primary reasons given for this include:

  • Using query_posts() can significantly slow down page load time by increasing by as much as double the amount of work required to process the query.
  • Since query_posts() replaces the standard query data it can cause any number of problems with pagination and wreak havoc on pages that make use of multiple queries.

In short, use of query_posts() is a dangerous proposition. As a matter of fact, the official documentation opens with the caveat: This function will completely override the main query and isn’t intended for use by plugins or themes. Its overly-simplistic approach to modifying the main query can be problematic and should be avoided wherever possible.

In other words, if you’re coding a theme or plugin — which is exactly what the vast majority of us are doing — avoid the use of query_posts(). Instead, create an entirely new WP_Query object or use get_posts(), get_pages(), or pre_get_posts instead.

get_posts()

Think of the get_posts() function as a modifiable, predefined instance of the WP_Query class, because that’s exactly what it is. When you use get_posts() you’re effectively calling up preset default values and using them to create an instance of the WP_Query class.

In one sense, get_posts() is a lot like query_posts(): they are both predefined instances of WP_Query. However, they are also quite different because query_posts() replaces the default $query object while get_posts() simply creates an entirely new query that does not interfere with global variables in the way that query_posts() does.

So, what’s the point in using get_posts()? Why not just use WP_Query? The answer is convenience. If you’re thinking of using get_posts() you could definitely accomplish whatever it is you’re trying to accomplish with WP_Query. However, by using get_posts() you save yourself a few keystrokes.

There is another difference between WP_Query and get_posts(). That is that the latter returns an array of posts while the former returns posts one at a time with the_post() function. That means that get_posts() returns all posts at once as an array while WP_Query iterates through posts one at a time echoing out the relevant content of each post as it iterates. Practically speaking, this means that when using get_posts() you use a foreach function to display the results, but you use the standard if...while loop structure to echo out content while using WP_Query.

The other thing to keep in mind is that since get_posts() does not modify the global $query object, you need to use setup_postdata() for each post to get access to WP_Query methods and properties. While that may sound complicated, it really isn’t. Have a look:

So that bit of code will return four posts from the slider category (presumably to display them in a post slider). First, we make sure that the query returned something with the if ( $slider_posts ) code, and then cycle through the posts displaying the title and content. By using setup_postdata() we’re able to use global variables such as $id and $authordata as well as template tags such as the_title() and the_content(). Without setting up post data, we would not be able to use those variables and template tags.

get_pages()

One query function that doesn’t get very much attention is the get_pages() query. Unlike all of the other queries in this post, which are all somehow related to WP_Query, get_pages() is a function that bypasses WP_Query and directly queries the database.

Like get_posts(), this function returns the content it locates in an array. However, unlike get_posts(), which can be used to pull up any posts of any post type (posts, pages, custom post types, and so forth), get_pages() can only be used to retrieve hierarchical post types such as pages and hierarchical custom post types.

In addition, get_pages() allows you to specify a few query parameters that are unique to hierarchical post types including 'child_of', 'parent', and 'hierarchical'. Access to these parameters is the primary reason why you might consider using this particular query.

Putting Theory Into Practice

In this post we’ve introduced five powerful WordPress query tools but we haven’t really shown you how to use them. However, we have written about writing custom queries in the past and you can learn more about how to use these queries in your themes and plugins by checking out these other resources from our blog:

Summary

As you can see, when it comes to querying the WordPress database, there is no shortage of options. There are at least five powerful tools that WordPress developers can use to get a hold of the database entries they wish to display. Let’s recap each of the available options:

  • WP_Query: the versatile class that powers most WordPress queries. It is flexible and can be used to create any sort of query.
  • pre_get_posts: a hook which can be used to refine the $query object before the database is queried, thereby safely modifying the default query. Use it along with conditional tags to refine the results of the standard query.
  • query_posts(): a powerful instance of the WP_Query class that replaces the default $query object and is not intended for theme or plugin development. Don’t use it.
  • get_posts(): an instance of the WP_Query class that returns an array of posts. It includes a range of default values that may save you a few keystrokes versus crafting a custom instance of WP_Query, but only if the default options fit your needs.
  • get_pages(): a function that can be refined through the use of hierarchical parameters and returns an array of hierarchical post types, such as pages or hierarchical custom post types.
Do you have any questions about using WordPress queries? Share them in the comments section below!

Tags: