Custom post types give WordPress developers the flexibility to store different kinds of data without polluting built-in posts and pages. That data is useless, however, if we are not able to display them where we want it. Enter the WP_Query class. It is a powerful tool that can fetch all kinds of data from the WordPress database. Here, I’ll show you how to use the WP_Query class to construct a loop to retrieve custom post types.
When to use the WP_Query class
The WP_Query class is commonly used to get custom post type data outside of the default loop. It allows you to create multiple loops on a single page. If you are trying to change the default query parameters for a post page, then you should use the pre_get_posts filter.
Suppose you are working on a site that uses the
superhero custom post type. You need to display the latest workshops on your homepage.
A quick reminder – the basic structure of a loop looks like this:
<?php $the_query = new WP_Query(); ?> <?php if ( $the_query->have_posts() ) : ?> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <!-- Do something here --> <?php endwhile; ?> <?php wp_reset_postdata(); ?> <?php endif; ?>
Note that the WP_Query class takes arguments in an array format, but if they are not defined, then WordPress will fall back to the defaults. To query for custom post types, all you have to do is to define
post_type in the argument list. In our case, we will create a new array to store our arguments.
<?php $args = array( 'post_type' => 'superhero', 'posts_per_page' => 3 ); $the_query = new WP_Query( $args ); ?> <?php if ( $the_query->have_posts() ) : ?> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <h2><?php the_title(); ?></h2> <?php endwhile; ?> <?php wp_reset_postdata(); ?> <?php endif; ?>
Your HTML output might look like this:
<h2>Wonder Woman</h2> <h2>Iron Man</h2> <h2>Captain America</h2>
From top to bottom, here’s what the loop is doing:
First, we created an array called
$args that contains our arguments. Inside the array we defined
3. Next, we kick off the loop by including our
$args array while creating an instance of the
WP_Query class. Then we loop through all the found posts and display the title for each post.
In our next example, we’ll add more parameters to our argument list and display more than just the post title:
<?php $args = array( 'post_type' => 'superhero', 'posts_per_page' => -1, 'order' => 'ASC', 'category_name' => 'marvel' ); $the_query = new WP_Query( $args ); ?> <?php if ( $the_query->have_posts() ) : ?> <div class="superhero-container"> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <div class="superhero"> <?php if ( get_the_post_thumbnail() ) : /* Show the featured image if there is one */ ?> <?php the_post_thumbnail(); ?> <?php endif; ?> <h2><?php the_title(); ?></h2> <?php the_content(); ?> </div> <?php endwhile; ?> </div> <?php wp_reset_postdata(); ?> <?php endif; ?>
Here we added a few more parameters: I set the
ASC which will list the heroes in alphabetical order starting from A. I also limited the category to
marvel so we only retrieve Marvel superheroes. For a more detailed parameter reference, check out the official docs.
It’s not unusual to set up the loop only to find that nothing displays. Some common mistakes I have come across are:
Make sure your custom post type slug is correct
The WordPress standard is to use singular names for post type slugs, e.g.
page. I try to follow the convention while setting up my own custom post type. Double-check whether your slug is singular or plural, and whether there are any prefixes.
Don’t forget wp_reset_postdata
Since we are creating a custom loop outside of the main WordPress loop, we need
wp_reset_postdata() to put everything back into place. Omitting this line will cause a lot of headaches!
Don’t use the $wp_query variable
$wp_query is the variable name used for the main WordPress loop. It’s possible to overwrite it by assigning your custom loop to that variable:
$wp_query = new WP_Query( $args ); Using
wp_reset_postdata() should reset everything, but I generally avoid naming my queries
$wp_query just in case.
$my_query, or something of your own choosing are totally fine.