WordPress Development for Intermediate Users: Queries and Loops

WordPress Development for Intermediate Users: Queries and Loops

The WordPress loop is a very powerful piece of code. You use it to access the database, find content based on given criteria, and then output that content in whatever way you need to.

This is the fifth post in our WordPress Development for Intermediate Users series. This series follows on from our popular WordPress Development for Beginners tutorials, which introduced you to the fundamentals of developing websites with WordPress, how to get started coding with PHP, and building themes and plugins.

In this part of the series, we’ll move on to the element of WordPress that makes content from the database show up on the pages of your site: queries and loops. We’ll take a quick look at the standard loop then move on to identify ways to customize it and write your own bespoke queries. You’ll learn about:

  • The standard loop, what it does and why you should treat it with respect.
  • The components of a query and loop.
  • The methods for creating custom queries and when to use each (including one you shouldn’t use).
  • Modifying the loop in a template part file.
  • Using WP_Query to create advanced custom queries, including worked examples which you’ll create using plugins that hook to the theme we’ve already coded.

So, let’s start with the standard loop.

Missed a tutorial in our WordPress Development for Intermediate Users series? You can catch up on all seven posts here:

That’s right, tons of WordPress knowledge, peer review, instructor feedback and certification, for free for WPMU DEV membersStart Learning

The Loop Defined

Here’s how the Codex defines the Loop:

The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags.

The Loop does five things:

  1. It runs a query to fetch content from the database based on a set of criteria (more of which shortly).
  2. It checks if the query has found any content.
  3. It runs through the first post (other item of content) and outputs the content from that post defined by the code in the loop.
  4. It runs through step three again for each post that’s been retrieved (where relevant).
  5. It stops and (sometimes) resets itself.

You’ve already worked with a loop if you’ve been following along with this series: in part one of this series, we created a loop.php file for our theme to contain the Loop, and edited one version of it. You’ll have seen from that example that your theme doesn’t just have to run one version of the loop; it can run multiple versions depending on the content being viewed. So you could have one loop for single posts, one for archives, another for pages and so on. You can also define loops according to the category, post type and more, by using the relevant template file.

You can code your loop directly into a template file or you can use a template part such as loop.php. I find using a template part gives me more control over my code and also lets me use the same loop in multiple template files, by including them using the get_template_part() function. You learned about this in part one of this series – if you need a refresher, go back to that.

Let’s take a look at the different parts of the loop. Use the single.php and loop.php files you created in Part 1 as a guide: if you don’t have your own copy of those, you’ll find them in the source files for the series.

Fetching Data: the Query

In a standard loop, you use this line to fetch data from the database:

In our template, this is in each template file, e.g. page.php and single.php. This checks if there are posts for the currently viewed content type, and then if so loops through each post using while( have_posts() ) : the_post();.

This is the standard query (or main query) that WordPress uses in template files to find the database content that applies to the content currently being viewed. So if you’re on a single post or page, it will use that post or page’s ID to fetch the single item from the database, while if you’re on an archive page it will use the category, post type, tag, taxonomy term, date or author ID to fetch all posts in that archive. WordPress handles this automatically for you: you don’t have to specify in the query what it is you want to display.

However, sometimes you might want to run a custom query, one that fetches content other than the one that relates to the currently viewed page. For example, you’re in a single post and you want to output a list of the latest posts after the post content, or a list of posts in the same category. This is possible: to do this you write a custom query using the WP_Query class, which you’ll learn about in more detail shortly.

The query looks a bit different if you’re coding a bespoke query using WP_Query:

You can see that there’s some extra code here:

  • The $args variable contains an array of arguments that define what kind of posts we’re looking for. This can include arguments such as post type, category, post date, number of posts, order in which they’re fetched and more.
  • Inside the if ( $query->have_posts() ) {} statement and before running through each post, you might want to include extra content such as a title for your list of posts or an opening ul element if the posts are going to be in a list. You then close this after the posts are run.
  • Also inside the if{} statement, you run a loop very similar to the standard loop, using while ( $query->have_posts() ) : $query->the_post();. This runs a loop in the same way but is based on your $query variable that you’ve defined and not on the main query.

Once you’re running through the loop for each post, the tags and markup you use to output content are exactly the same as for a standard loop.

It’s also possible to amend the main query without writing a new one. To do this, you use the pre_get_posts hook, which is run very time WordPress runs a query. Instead of adding this to the relevant template file, you write a function (either in your theme’s functions file or in a plugin) that amends the query in a given set of circumstances, which you specify using a conditional tag. I’ll show you how to do this in a little while.

Displaying Content: the Loop

Once you’ve defined your query, you then run through the loop for each post that’s fetched. Inside that loop you can use whatever markup you like to add in elements, IDs and classes, plus static text and a variety of template tags that let you display content that’s been fetched from the database. These include:

  • the_title() – the title of a post or page
  • the_permalink() – the URL of that post or page’s single page. Use this in a <a> element around the title, the featured image and/or a ‘read more link’ if you add one manually.
  • the_excerpt() – this displays an excerpt of the post, which you either define manually in the post editing screen or which WordPress fetches for you using the first 55 characters of the posts content (you can change this value using the excerpt_length filter if you want).
  • the_content() – this outputs the content of the post or page
  • the_post_thumbnail() – display the post’s featured image if it has one. You can define the size of the image using a size attribute.

You can also display metadata about the post, including:

  • the_author() – displays the name of the post’s author in a link to that author’s archive page.
  • the_category() – displays a list of categories the post is saved under, in a link to each category’s archive page.
  • the_tags() – displays a list of tags the post is saved against, in a link to each tag’s archive page.
  • the_meta() – displays post metadata associated with that post (or custom fields).

The tags you use in the loop are the same whether you’re using a standard query or a custom one with WP_Query. Which ones you use depend on what you want to display on your pages – you might use different tags in archive pages than in single posts, for example. An archive page might include the excerpt while the single post has to contain the content.

Creating Custom Queries – Five Good Ways, One Bad

If you want to display custom content on a given page you can either customize the standard query or create your own. There are four ways to do this, and one additional method that you shouldn’t use (despite the fact that there are still people out there telling you to do so).

Customizing the Main Query

If you want to customize the main query you should use the pre_get_posts filter hook. This is run whenever WordPress runs the main query so you can hook into it to change the way the query runs.

Another method you may have seen in some books and websites is the query_posts() function. This an inefficient and potentially unreliable way of amending the main query. Instead of actually amending the main query it fetches the main query then throws it out and starts again, rerunning it 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!!

Creating a New Query

There are three ways to create a completely new query:

  • The get_posts() function, which will fetch all posts.
  • The get_pages() function, which will fetch all pages.
  • The WP_Query class, which you can customize to fetch whatever content type you like.

The get_posts() and get_pages() functions are the easiest and quickest way to fetch those particular content types, and can be more straightforward than WP_Query. But WP_Query gives you much more control over exactly what you’re querying, and lets you query all post types. The three methods work in essentially the same way behind the scenes so you can trust all of them to work well.

Beware using any of these methods instead of the main query when you could just customize the main query instead. As you’ll see shortly, you can use pre_get_posts to make changes to the main query in very specific cisrcumstamces using a conditional tag, so that might save you writing a custom query in a specific template file. So instead of writing a custom query for a particular category archive, for example, you could use pre_get_posts to amend the query for that category and create a template file for that category with a custom loop to output what’s fetched by the (amended) main query.

Let’s take a look at the most commonly used methods for modifying the main query or creating a new one.

Modifying the Main Query with pre_get_posts

You can use pre_get_posts for a number of things, including:

  • Adding custom post types to queries where they wouldn’t normally be included, such as the main blog page, category or tag archives or search results.
  • Removing a certain category or taxonomy term from a query for a certain post type or on the home page.
  • Amending the number of posts returned by the main query.

In fact, most things you might want to change about the main query can be done with pre_get_posts.

Let’s take a look at an example. You might remember that in part four of this series we added the Category taxonomy to Pages in our site. I’ve added a Page and assigned a category to it of “Our Team”:

Our team

But if I visit the category archive for “Our Team,” only the posts with that category assigned show up, not the pages:


We can change that using the pre_get_posts hook.

  1. Open your theme’s functions.php file.
  2. Add this function to it:
  3. Save the file.

This checks that WordPress is running the main query and also that we’re on a category archive using the is_category() conditional tag. If both of these are true, then it sets the query to include both posts and pages.

Now if you view that category archive again, your page will show up:

Category archive on the front-end

Creating a New Query with get_posts() or get_pages()

The get_posts() and get_pages() functions are useful when you want to run a new query to retrieve just posts or pages. While they are limited to just one post type, using them can be quicker than using the WP_Query class.

Let’s take a look at how you might use the get_posts() function to retrieve a list of the most recent posts and output them via a hook in your theme. For this I’m going to create a plugin with a function that I’ll hook to one of the action hooks that I’ve already added to my theme.

Note: the get_posts() function works in the same way as get_pages() so you can apply what you learn here when using that function too.

I don’t want this code to run on my main blog page because that already displays the latest posts, so I’ll ad a conditional tag to check that.

Start by creating a new plugin and activating it on your site. If you don’t have a hook after the content in your theme, then add one. You’ve learned how to do both of these things already in this series. If you’re using the theme provided with the series’ source files, then the hook will already be in place.

Once you have your plugin set up, add this to it:

That sets up your function, hooks it to the correct action, and adds the conditional tag inside it.

Now inside the conditional tag, add the arguments for the get_posts() function:

Under that, run the get_posts() function plus a check that any posts are returned by it:

Inside the braces add this:

Let’s take a look at what that does:

  1. It adds a heading.
  2. It opens a foreach loop to run through each post in turn.
  3. Inside the foreach loop, it uses setup_postdata to set up the post (without this we can’t output the excerpt).
  4. Inside the loop it defines the post ID for the current post and assigns it to the $postID variable.
  5. Using that $postID variable as the parameter, it uses the get_the_permalink(), get_the_title() and the_excerpt() functions to output each of these. You can’t use the_permalink() etc. here as those only work in the main loop or one you define with WP_Query. Note that you use the_excerpt() instead of echo get_the_excerpt() because get_the_excerpt() can’t take the post ID as a parameter.
  6. It closes the foreach loop, which then runs again for each subsequent post.

Now save your file. Your full function will look like this:

If you visit any page in your site other than the main blog page, you’ll now see your latest posts after the content:


The styling isn’t great right now so I’m going to add some margins to my theme’s stylesheet to space things out a bit. You can find the styling in the source files for the series.

Here’s my list of recent posts now:

Recent posts

If you wanted to you could take this further, adding a stylesheet to your plugin (and enqueueing it correctly, as you learned earlier in this series) for specific styling or just adding styling to your theme’s stylesheet.

Creating an Advanced Custom Query with the WP_Query Class

So far you’ve amended the main query using pre_get_posts and you’ve queried just posts using get_posts(). But what if you wanted to run a query based on something else?

Well, you can. The WP_Query class lets you query just about anything you like: post types, taxonomy terms, publication dates, authors, metadata and more. You’ll find detailed guidance on its arguments in the Codex and useful examples of how to use it in this series on mastering WP_Query.

I’m going to show you how to use WP_Query to query the latest project and add it to the sidebar. So let’s do it!

Note: This will only work if you’ve added the Project post type which we did in the previous part of this series. If you haven’t done that, check out the source code for the plugin that adds this.

First, create another plugin. I’m not adding this code to the theme’s sidebar.php file but instead I’m going to hook it to the wpmu_after_sidebar hook that we created in our theme. If your theme doesn’t have a hook in the sidebar you’ll need to add one.

Instead of putting your theme in a file of its own, put it in a folder and add a subfolder for styles along with a style.css file in that folder. Enqueue the stylesheet in the way you did in Part 3 of this series. I’m not going to repeat this as you’ve already learned it, but if you get stuck take a look at the source files for this part of the series.

So your plugin will have a folder with a stylesheet, plus the plugin file in which you’ll have the opening commented out text and the function to enqueue the stylesheet. If you’ve copied one of your earlier plugins, make sure you give everything a unique name – the plugin file and folder as well as the function to enqueue the stylesheet.

In your plugin file, add this:

That’s your empty function attached to the relevant action hook. Now let’s start to populate that function. First, add the arguments for the query:

Now run the query and add a check for posts:

Next, add a loop to output content if posts are found, inside the braces:

Note that at the end of that loop I added the rewind_posts() function. You must always add this after running a query using WP_Query, so that WordPress can reset itself and go back to working with the main query for the current page.

Save your file. Here’s the full function you should have:

Now if you look at your site you’ll see the latest project in the sidebar:

Latest project in the sidebar

That’s showing up in the sidebar but it could look better. I’m going to add some extra styling to make it more prominent, in the stylesheet for the plugin. Here’s my code:

You can style it however you want – knock yourself out, I’m sure yours will look much prettier than mine!

Here’s my final version:

Latest project in the sidebar

So that’s how you use WP_Query to fetch the most recent post of a given post type. You could do more with this, for example:

  • On a service archive page, remove that service form the query so it isn’t duplicated in the sidebar.
  • Have a different project displayed each time the screen is refreshed.
  • Use another taxonomy for featured projects and display only those.
  • Use a conditional tag to only run the query on Pages or the main blog page, for example.

There are lots of possibilities – why not try some!

Queries and Loops Are What Make WordPress Tick

Without the loop, your sites would just have empty pages with no content. As you’ve learned here, WordPress passes a query on every page and then runs a loop to output the posts fetched by that query.

The main query is the one which WordPress runs by default: it identifies what’s currently being viewed and queries the database accordingly. But you can write your own queries too. You can either modify the main query using pre_get_posts or use the get_pages() or get_posts() functions or the WP_Query class to write your own.

Custom queries give you much more control over what’s displayed in your site, and with them you’re not limited to displaying the default content on a given page. I use them often to add extra post listings after the content or in the sidebar, to display posts by taxonomy term rather than just in date order, and for many more uses. Why not come up with your own custom queries and give it a try!

In the next part of this series, we’ll look at metadata. WordPress uses various types of metadata to store extra data for posts, comments and authors. You’ll learn how it works, how to display it on your site and how to code custom metaboxes for your users to add metadata via the admin screens. See you next time!

Did you find this tutorial helpful? Why do you want to learn WordPress development? What do you want to know more about? Let us know in the comments below.

Rachel McCollin

Rachel McCollin Rachel is a freelance web designer and writer specializing in mobile and responsive WordPress development. She's the author of four WordPress books, including WordPress Pushing the Limits, published by Wiley.