WordPress has a ton of built-in functionality, and one of the neatest things is the ability to query posts and data in many different ways. The WP_Query class is at the heart of all that power, giving you access to everything from post titles to author information.
WordPress provides several built-in ways for users to leverage this information, including categories, tags, authors, and search bars. However, what if you want to provide a more curated list of posts for site visitors?
That's when you turn to the WP_Query class.
What is WP_Query?
WP_Query is a PHP class for constructing queries to the WordPress database and returning posts, pages, or other custom objects to render on the page. It allows developers to build complex searches while removing the need to write separate SQL queries.
WP_Query provides shortcuts and built-in functions for customizing The Loop, which is the process for displaying content on a given page. With this focus on The Loop, WP_Query is the preferred choice for WordPress theme developers seeking to customize the content and appearance of pages.
Given the importance of The Loop, we will examine it alongside the basic structure of a WP_Query call.
How to Use WP_Query
Before examining The Loop, let's take a quick look at the WP_Query syntax below. At its most basic level, you call WP_Query and invoke a new instance of the class.
However, any results pulled by WP_Query can only be manipulated if assigned to a variable.
Now you have the $posts variable to store the array of content pulled from the database. This will also allow you to interact with these results in the next section.
However, it would be important to emphasize that invoking the WP_Query class without providing any arguments will not pull any content from the database. The many parameters available for WP_Query will be examined in a later section. For now, let's add the cat parameter (short for category) with an argument of 2, which is the ID of the fiction category.
You may have noticed that the cat parameter is wrapped inside an array. This means that you can provide multiple arguments to WP_Query to refine your search.
Now let's look at how you would display your results with a custom version of The Loop.
WP_Query and The Loop
In normal WordPress, The Loop determines how content is rendered on the page. You can see a high-level representation of The Loop below.
In this workflow, the have_posts() function checks if there are posts to be displayed. If there are, then a while loop executes to take each post and retrieve its data with the the_post() function. That data is then rendered onto the page through HTML and additional PHP. The while loop continues to execute while there are posts to be displayed.
The Loop logic in the previous example is repurposed when you need to display the results of a WP_Query call. Let's inspect one example of a customized loop.
In this example, you define your arguments and invoke a new instance of the WP_Query class to search the database. The while loop then iterates over the array of returned results. the_title() and the_content() functions pull the title and content of each blog post, and these are rendered to the page with echo statements.
This example also has a catch-all if no posts in the database match the provided arguments with the else statement. Otherwise, no content would display on the page.
Finally, you'll notice the wp_reset_postdata() function call at the end of the file. This best practice will be examined in the next section.
Potential Pitfalls and Solutions of WP_Query
Though WP_Query is powerful, it has a potential drawback that can lead to significant site issues. Fortunately, there are two solutions outlined in the following sections.
1. Reset the post data.
In the last example, the custom loop file ended with wp_reset_postdata(). This function call is necessary if your loop uses the_post(). The wp_reset_postdata() function resets the global $post object, so there is no overlap in the posts displayed by other loops.
For example, if you add a custom loop for a widget on the main feed of your blog, the loop building the feed may display a post from your query when it resumes versus the next post in its own array.
2. Store the global post object.
An alternate workaround for this pitfall is to store the global $post object in a temporary variable. This variable is then used to restore the $post object by reassigning its values, as you can see below.
The WordPress Codex recommends using wp_reset_postdata(), but both options are viable.
The potential conflict between WP_Query custom loops and The Loop is one reason WordPress plugin developers prefer the get_posts method of the WP_Query class. get_posts pulls the same content using parameters like WP_Query, and the reset process happens automatically with each call.
Now that you have customized The Loop and mitigated any pitfalls let's review the different search criteria available to you in WP_Query.
The true power of WP_Query comes from the variety of parameters available to refine your database searches. The values specified for these parameters are called arguments (often shortened to args).
WP_Query has 18 different parameter groups to pull from:
There are too many categories to walk through individually, but the next section inspects the most commonly used parameters. You can review the parameter groups in greater detail in the WordPress Codex.
WP_Query Common Parameters
Below are the parameters you will likely use in your searches:
- posts_per_page: declares the number of posts you want returned
- author: dictates whether posts belong to one or more authors based on IDs
- cat: dictates whether results should fall under one or multiple categories based on IDs
- tag: determines if posts should have one or more tags as defined by tag slugs
- orderby: defines the criteria that you want the retrieved posts ordered by (e.g. by date, by number of comments, etc.)
- order: determines if the results are sorted in ascending or descending order
- post_type: decides if WP_Query retrieves posts, pages, or custom post types
- post_status: determines if the query retrieves published, scheduled, in-progress, or deleted posts
You've already seen the cat parameter in action in the custom loop. To review, parameters are added to the arguments array with their specified search values. You can see another example of a parameter below.
Here, the query searches for posts that have both the "short story" and "action" tags. If we want to make this query either/or, we can replace the plus (+) sign with a comma (,).
Now, the search pulls posts with either the "short story" or the "action" tags. The string with values separated by plus signs or commas is one example of how you can submit multiple search terms in WP_Query to widen or narrow your search.
Let's examine other ways you can submit multiple values in one argument.
Using Multiple Values in Arguments in WP_Query
You will often find yourself in situations where you'd like to search for more than one value in an argument. For example, you can fetch posts from multiple authors by adding an array to the author__in parameter. The array contains two author IDs to form an inclusive range, so the posts from authors with IDs 1-5 are pulled in the example below.
Other parameters, like post_type, also take an array with strings instead of IDs. You can see this in action in our next example; the arguments tell WP_Query to pull posts, pages, and testimonials from the database.
Some parameters don't take an array at all. Instead, they accept a string of values. As you saw with the tags property, these values are separated by a comma for "either/or" searches or a plus sign for "and" searches.
In the example below, the query searches for posts in the category with an ID of 12 or posts that are not in the category with an ID of 13. Adding the negative sign to 13 denotes that this category should be excluded.
Now that you understand the options available to refine your searches, let's examine some of the default parameters in WP_Query.
WP_Query Default Arguments
WordPress makes assumptions for the arguments of certain parameters. This saves you from defining common arguments each time you call WP_Query and shortens your code, which leads to better performance and easier debugging.
However, you should be aware of these default arguments to better understand how WP_Query sorts and returns content and redefine them as needed. The critical WP_Query default arguments are:
- post_type defaults to "post."
- post_status defaults to "publish" for published content.
- order defaults to "DESC" for descending.
- orderby defaults to "date."
There are many nuances to the different parameters and arguments used in WP_Query. When in doubt, consult the Codex.
Now, let's put these parameters into action and submit some real-world queries to your database.
For most WordPress sites, you will want to pull different types of content to grab your users' attention or serve their needs. Naturally, WP_Query is suited to the job. The following examples walk through common queries you may use on your site.
1. Get posts published this week.
Your users are usually interested in your latest content, especially if your site focuses on news or a fast-evolving industry like IT. Fortunately, WP_Query has a powerful system for finding posts based on date parameters.
In this example, the query searches for posts published this week. We use the date_query parameter to achieve this, which accepts multiple date arguments in its array.
To target the current week, a second array is nested inside the date_query array with the year and week properties. These values are set with the built-in date function. The "Y" and "W" arguments are shorthand for the current year and week. This means the query will pull for the current week without any updates to ensure your site visitors always see the latest posts.
If you want to target additional dates, you can add additional date_query arrays, as demonstrated below.
Now, the query targets posts from the current week and any posts published on December 14, 2021.
2. Get posts by the same author in the same category.
Next, let's examine another common content offering. A reader has finished reading an article, so you want to provide a row of similar posts if they're interested in reading more. To do this, the query will search for posts by the same author and in the same category as the current post.
The example searches for posts that have an author_name matching the user_nicename "stephen" and that match the "fiction" category_name argument. Since the related posts widget typically shows three posts, the example sets this limit in the posts_per_page parameter. This leads to better performance because the query is not pulling extraneous post data.
Note that WP_Query defaults to sorting results by date in descending order, which means the most recent posts matching these criteria are displayed. If you want to display results based on different parameters, you would adjust the orderby and order parameters in the sample code above.
The next example will overwrite the order by default.
3. Get popular posts in the same category.
Similar to the last example, you may want to provide additional posts on a topic that your readers are engaging with. You can achieve this using the orderby parameter and setting it to the comment_count argument.
The query searches for posts in the fiction category, sort the results by the number of comments and displays the five posts with the most comments (since most sidebar widgets cut off at five).
Once again, the default order parameter is left in descending so that the post with the highest engagement displays first.
Use WP_Query to build new experiences.
WordPress's database provides a wealth of opportunities to search through your site's content and discover new offerings for your users. WP_Query is an effective way to construct these queries and display the results on your page.
Given the layers of parameters and arguments available to refine searches, WP_Query is an ideal method to discover connections between your posts and improve the user experience for your readers through timely and relevant content offerings.