On every page of your website, WordPress runs a query. This fetches data from your site’s database and then displays it in the way your theme tells it to using the loop. This is referred to as the main query.

Depending on the type of page being displayed, WordPress will use the most appropriate template file, meaning that the loop might vary for different content types. But it will always run a query to fetch data from the database.

Sometimes you may want to change the way the query works. For example, on your main blog page you may want to exclude posts in a certain category, or on an archive page you may want to list posts by category rather than by date order. You could also decide you need to add extra queries to your pages, adding lists of recent posts or related posts, for example. Or you may want to create a template file which replaces the main query with a completely custom one.

The great news is that WordPress makes this possible. There are a few methods you can use to either modify the main query or create a new one.

In this post I’ll look at:

  • Scenarios in which you might need a custom query, focusing on which require customizing the main query and which require a whole new query, and
  • The five methods for creating custom queries (including one you shouldn’t use, and why).

Continue reading, or jump ahead using these links:

Understanding the Basics of Custom Queries in WordPress

If you haven’t created custom queries before, there are a few terms you’ll need to understand. If you’ve worked with theme template files or queries before, you can skip this bit!

  • A query is a routine, which WordPress runs to fetch data from your site’s database. This will include posts, attachments, comments, pages, or any content that you’ve added to your site.
  • The loop is code your theme (or sometimes a plugin) used to specify how the results of the query will be displayed on the page. So for example on your main blog page the loop might include the title of each post, a extract, a featured image maybe, and a link to the post’s own page (called the permalink).
  • Template files are used by your theme to display pages for each content type. Different themes have different template files, but they must include a main index.php file and will often also include a page.php file for static pages, a single.php file for single posts, and archive.php file for archive pages and maybe a category.php file and tag.php file for category and tag archives respectively. There are more template files themes can use, for example to dimply taxonomy or post type archives – for more details see the Template Hierarchy.
  • Conditional tags can be used in your template files or by plugins to check what kind of page is currently being displayed. For example is_page() checks if a static page is being displayed and is_home() checks if we’re on the home page. There are plenty more conditional tags including ones for whether the user is logged in and more.

Custom Query Types and When to Use Them

There are two types of custom query:

  • The main query as called by WordPress, but with changes that you’ve made to it, and
  • A completely new query to fetch different or extra content.

Let’s take a look at when you might use each.

Modifying the Main Query

This is something you’d do if you want your page to display the results of the main query for that content type, but you want to make some tweaks to it. You don’t want to show completely different content and you don’t want to add an extra loop.

Examples of this might include:

  • On your main blog page, displaying custom post types as well as posts
  • On a category archive page, only displaying posts of one post type
  • On a category archive page, ordering the posts displayed alphabetically instead of by date

There are plenty more possibilities, but as you can see, this is about making minimal changes to what’s queried or to the way in which it’s output.

Later in this post, I’ll show you how to achieve all of the above.

Writing a New Query

If amending the main query isn’t enough, you’ll need to create a new query. This gives you much more flexibility but shouldn’t be used when you just want to modify the main query, as it’s less efficient. You would create a new query if you want more than one loop on a page or if you want to overwrite the main query with a completely new one.

Examples of when you might need to create a new query are many and varied, but include:

  • Running two loops on an archive page: one for the first post and another for all subsequent posts. You would do this if you want to display different content for the first post, for example if you want to include an excerpt or featured image for the first post but not for the others. Note that if all you want to do is style the first post differently, it’s unlikely you’d need multiple loops: instead you should be able to use CSS targeting the first post.
  • On a single post page, running an extra loop to display other recent posts (or featured posts) below the post content, to encourage your readers to read more.
  • Adding a banner linking to a single featured post (or to all featured posts) at the top of each of your pages, for example if you’ve added a post promoting a new product. This gives you more flexibility than adding a static banner as you can change the post used more easily.
  • Creating a list of pages in the same section of the site, if your site has a structure based on hierarchical pages. You might want to put this in the sidebar.
  • Creating a page template with a completely custom query to display posts by taxonomy or post type (or maybe both, in a grid).
  • On a post type archive page, listing posts by category or taxonomy term rather than by date (for example creating columns or boxes with links to recent posts in each category).
  • Creating a banner in your sidebar to link to the most recent post and display its featured image.
  • Creating a page to list posts with a specified term in more than one taxonomy (e.g. on a movie site, listing sci-fi films made in the USA, with genre and country each being a taxonomy).
  • Creating a post type for sidebar content and querying posts of that post type in the sidebar. This would help non-coders to add content to the sidebar with more flexibility than they can get from widgets.

There will be many more scenarios that I haven’t included here, but this gives you an idea. I won’t be showing you how to achieve each of these in this post as there would be way too much to cover, but I will give some examples.

The Methods for Creating a Custom Query

There are five methods for creating custom queries, and they can be split according to whether they help you modify the main query or create a new one. The methods for modifying the main query are:

  • Using the pre_get_posts action hook. This lets you make modifications to the main query by adding a function to your theme’s functions file or via a plugin (not in your theme template files). You can combine it with a conditional statement to ensure it only runs on pages displaying certain content types.
  • Using query_posts(). I’ve included this here partly for completeness but more importantly to explain why you shouldn’t use it. query_posts() is an inefficient and potentially unreliable way of amending the main query. Instead of actually amending the main query it fetches the main query then chucks it out and starts again, rerunning the main query with your changes. This will slow down your site. It’s also unreliable and can break, especially when pagination is needed. So don’t use it!!

The three remaining methods all let you create a new query:

  • The WP_Query class. This is the most powerful and flexible way to create a new query, and you would use it when creating a second loop in a template file or when creating a template file with a completely custom query replacing the main loop. You have to be careful when using it: the main risk is if you don’t reset the query after running your loop, which means that WordPress won’t be able to correctly identify what page type is being displayed. But you can easily get round this.
  • The get_posts() template tag. You would use this in a template file (including your sidebar or footer for example) to fetch a list of posts. It uses the WP_Query class to do this, so is in effect a simpler way of using that if all you need is posts. You can use parameters with it to specify which posts you want.
  • The get_pages() template tag. This works in the same way as get_posts(), fetching pages instead of posts.

So now you know what the five methods are, let’s take a look at each of the recommended ones in detail.

The pre_get_posts Action Hook

pre_get_posts is an action hook, which means you attach a function to it to make something happen at the time that WordPress runs the pre_get_posts action. As you might expect, WordPress runs this action immediately prior to fetching posts from the database, so any function you attach to it will affect how WordPress does that.

To use pre_get_posts, you create a function and then hook it to the action, like so:

Let’s take a look at what this does:

  • Firstly, it creates a function called my_function. What the function does is contained within the braces.
  • Next, it attaches that function to the pre_get_posts hook using the add_action() function. Without this, your function won’t work.

In addition to this, you almost always need tho include a conditional tag inside your function. Without this, WordPress will run your function every time it fetches posts, including when you’re working on your posts in the admin. So your function will look like this:

Above I’ve checked that we’re not in the admin screens and also that the query being run is the main query. It’s important to check WordPress is running the main query as it might cause problems if you run your function for extra queries you’ve created. You can add extra conditional tags here as we’ll see. Let’s flesh this out a bit with some examples.

Including Custom Post Types On Your Main Blog Page

By default, WordPress only lists posts on the homepage. If you create a custom post type, it’ll assume you want to display those elsewhere and not include them here. But sometimes you may want to display more than one post type on the home page, in which case you use the pre_get_posts hook.

To do this, you add the following to your theme’s functions.php file or to a plugin you create:

This checks for two things: is this the main query and is it the home page (using is_home()). Then it sets the query to include two post types: 'post' and 'custom_post_type', being your custom post type. Note that you must include 'post' here if you still want the home page to display posts as well as your custom post type. If you just added 'custom_post_type' here, this would override the default and just show posts of your custom post type. Sometimes you might want to do that but this isn’t one of those times.

You can read about this technique in more detail in this post.

Display Posts of a Custom Post Type on a Category Archive Page

This example assumes that when you registered your custom post type, you gave it support for categories and that you’ve assigned categories to posts of your custom post type. To amend your category archives to display posts of your custom post type, you use the following:

This checks if the main query is being run and if we’re on a category archive using is_category(). It then modifies the query to fetch posts of your custom post type. Note that because I haven’t included 'post' here, ordinary posts won’t be displayed on any category archives, and that I don’t need to use an array as I’m only specifying one post type.

If you wanted to be more specific when using this method, you could check for a particular category:

This will modify the main query only on the 'category-slug' archive page, where 'category-slug' is the slug for your category.

Changing the Way Posts Are Ordered

Our final example deals not with what data is queried but with how it’s output. Let’s say that on your category archive pages you don’t want to display posts by date, but in alphabetical order. You can do this using pre_get_posts as follows:

This uses two query parameters: orderby and order, to modify both what posts are sorted by and the order in which they’re displayed. For more parameters you can use with pre_get_posts, see the WordPress Codex page for WP_Query, which uses the same parameters.

The WP_Query Class

The WP_Query class is the most powerful method available for writing a custom query. Use it when you want to replace the main query with a new one or when you want to add a new query in addition to the main query.

WP_Query has parts:

  • The arguments for the query, using parameters similar to the ones you might use for pre_get_posts
  • The query itself
  • The loop
  • Cleaning up: closing if and while tags and resetting post data.

This will look something like the following:

As you can see, this is more complicated than using pre_get_posts, which is one of the reasons you should avoid using it if you want to modify the main query. But the main reason for this is because that would work WordPress harder and slow your site down.

Let’s take a look at an example. Let’s say I want to add a second loop after the post content on my single.php template file, to display a list of featured posts. I’ve defined those featured posts using a “featured” category. For each of these posts I want to display the featured image and title, with links to the post.

Here’s how I do it:

This uses three arguments to query data:

  • 'post_type' => 'post' fetches just posts
  • 'posts_per_page' => '4' only fetches four posts
  • 'post__not_in' => array( $post->ID ) ensures that the post currently being displayed isn’t included.

It then outputs the four posts in a loop which displays the featured image and the title, each inside a link to the post’s page. You could then use CSS to style these, placing them side by side or in a grid and overlaying the title over the image.

The get_posts() Template Tag

If you don’t need as much flexibility as you get from WP_Query, you might find that get_posts() does what you need. In fact, I could have used it for the example above. get_posts() is a template tag which accesses the WP_Query class, and you can use it in a similar way to WP_Query.

So, to create that list of four recent posts that I created above with WP_Query, you’d use get_posts() in the following way:

The eagle-eyed amongst you will spot that this is very similar to the code I used with WP_Query above. However if you’re really observant you’ll have spotted a few differences:

  • The arguments don’t have to include the post type
  • I use a $posts variable to store the array output by get_posts()
  • Instead of checking if the query has posts I use if( $posts ) to check if it’s got anything in it
  • Instead of a standard loop I use foreach ( $posts as $post ), which loops through each row in the array
  • To access all of the post data I want, I include setup_postdat( $post ).

As get_posts() uses WP_Query, there really isn’t much difference between the two so I tend to use WP_Query as it gives me more flexibility (and because I’m used to using it with custom post types). However where I find get_posts() most useful is if I simply want to check if there are any posts with my arguments. I can then output code depending on whether there are any posts present, which doesn’t have to be a loop.

The get_pages() Template Tag

get_pages() is very similar to get_posts(): it uses WP_Query but it fetches static pages instead of posts. Let’s look at an example where you might use it.

Let’s say your site has a set of top level pages which are most important and you want to add a list of these in the sidebar so you can style their links and encourage visitors to go to those pages. In your sidebar.php template file, you’d add this code:

Let’s take a look through this:

  • First I define my arguments – 'parent' => 0 retrieves pages with no parent, while the other two arguments define how the pages are sorted.
  • I then use get_pages() to populate an array stored as $pages
  • I check if $pages has any data using if( $pages )
  • If so, I open a list, then for each page I open a list item
  • Instead of using setup_postdata() like I did with get_pages(), I make direct reference to the $post variable with different template tags that will output the link and the title. I have to use these because I haven’t used setup_postdata().
  • Because I didn’t use setup_postdata() I don’t have to use wp_reset_postdata().

The code above is a more efficient way of outputting a list of pages than if I’d used WP_Query in all its glory.

Conclusion

Being able to modify the main query or write your own queries is a very useful skill to develop if you’re planning on creating custom themes or plugins, or developing complex, data-driven sites for clients.

I use one form of custom query or another in almost every site I build, and in my view it’s one of the most exciting things about WordPress (but you may have your own favorites!)

As I’ve demonstrated here, there are five ways you could potentially create custom queries, although only four of these should be used. These are:

  • pre_get_posts for modifying the main query
  • WP_Query for creating complex custom queries
  • get_posts() and get_pages() for simpler custom queries retrieving just posts or pages.

A combination of these will help you to create advanced WordPress sites and display data however you need to.

Do you use custom queries? What are your top tips for creating custom queries? Let us know in the comments below.

Tags: