Tutorials UPDATED: 30 June 2023

WordPress Custom Queries – Using the WP_Query Class

Tassos Antoniou

9 min read
WordPress Custom Queries - Using the WP_Query Class

WordPress offers a wide range of ready-to-use functions that in most cases can retrieve the necessary information from the database. But when developing a WordPress website there are numerous scenarios where a filter or action isn’t sufficient to achieve the desired result. In these cases. WordPress offers a workaround in the form of the WP_Query class.

Let’s take a look at just how the WP_Query class can be used!

Custom Query Cases

NOTE: This article assumes you have some knowledge of PHP and MySQL/MariaDB plus are familiar working with WordPress.

Whatever page you visit, WordPress runs what is called a ‘main query’ to display the content. So when you visit a page like a category view for example, a WP_Query object is created behind the scenes and retrieves all the necessary data from the database to display the page.

What the WP_Query basically does is offer you the ability to retrieve content from your website’s database without having to use SQL queries.

To do this, all we need to do is define the arguments corresponding to our needs and the a new WP_Query object will be created and translated into an SQL query.

An Example Using WP_Query

If we want to display posts from a specific category, within another category template, then creating a new query object is necessary.

For the purposes of this example we activated the Twenty Twenty theme and created 2 posts under a category named ‘Category 1’ and 2 posts under a category named ‘Services’.

In addition, we created a custom category view template for the ‘Category 1’ posts. The slug of this category is ‘category-1’ therefore the file under the theme’s folder should be category-CATEGORYSLUG.php which in our case is category-category-1.php.

Please paste the following in the category-category-1.php file.

<?php
/*** Custom Category 1 Template */

get_header(); ?>
<main id="site-content" role="main">
<section id="primary" class="site-content">
<div id="content" role="main">

<?php if ( have_posts() ) : ?>
 
<header class="archive-header">
<h3 class="archive-title">Category: <?php single_cat_title( '', true ); ?></h3>
</header>
 
<?php while ( have_posts() ) : the_post();
	the_title( '<h4 class="example"></h4>' );
endwhile;

endif; 
?>
</div>
</section>
</main>

<?php get_sidebar(); ?>
<?php get_footer(); ?>

Before the loop we use the have_posts method to determine whether there are more posts available in the loop.

The the_post() is necessary for the loop to iterate the posts and tell WordPress to move to the next post of the posts array.

At this point our frontend view of the category (https://mycompanyname.com/category/category-1/ ) will be something like this:

I know that by using this piece of code we’ve neglected to include a lot of post information, but for the sake of this example, it is better to keep it simple and to only display post titles.

To see the query object properties that WordPress created for this view we can simply add this line in our code after the endif; statement.

var_dump ( $wp_query->query_vars );

Now if you refresh your page you will this info under the posts list:

array(63) { ["category_name"]=> string(10) "category-1" ["error"]=> string(0) "" ["m"]=> string(0) "" ["p"]=> int(0) ["post_parent"]=> string(0) "" ["subpost"]=> string(0) "" ["subpost_id"]=> string(0) "" ["attachment"]=> string(0) "" ["attachment_id"]=> int(0) ["name"]=> string(0) "" ["pagename"]=> string(0) "" ["page_id"]=> int(0) ["second"]=> string(0) "" ["minute"]=> string(0) "" ["hour"]=> string(0) "" ["day"]=> int(0) ["monthnum"]=> int(0) ["year"]=> int(0) ["w"]=> int(0) ["tag"]=> string(0) "" ["cat"]=> int(16) ["tag_id"]=> string(0) "" ["author"]=> string(0) "" ["author_name"]=> string(0) "" ["feed"]=> string(0) "" ["tb"]=> string(0) "" ["paged"]=> int(0) ["meta_key"]=> string(0) "" ["meta_value"]=> string(0) "" ["preview"]=> string(0) "" ["s"]=> string(0) "" ["sentence"]=> string(0) "" ["title"]=> string(0) "" ["fields"]=> string(0) "" ["menu_order"]=> string(0) "" ["embed"]=> string(0) "" ["category__in"]=> array(0) { } ["category__not_in"]=> array(0) { } ["category__and"]=> array(0) { } ["post__in"]=> array(0) { } ["post__not_in"]=> array(0) { } ["post_name__in"]=> array(0) { } ["tag__in"]=> array(0) { } ["tag__not_in"]=> array(0) { } ["tag__and"]=> array(0) { } ["tag_slug__in"]=> array(0) { } ["tag_slug__and"]=> array(0) { } ["post_parent__in"]=> array(0) { } ["post_parent__not_in"]=> array(0) { } ["author__in"]=> array(0) { } ["author__not_in"]=> array(0) { } ["ignore_sticky_posts"]=> bool(false) ["suppress_filters"]=> bool(false) ["cache_results"]=> bool(true) ["update_post_term_cache"]=> bool(true) ["lazy_load_term_meta"]=> bool(true) ["update_post_meta_cache"]=> bool(true) ["post_type"]=> string(0) "" ["posts_per_page"]=> int(10) ["nopaging"]=> bool(false) ["comments_per_page"]=> string(2) "50" ["no_found_rows"]=> bool(false) ["order"]=> string(4) "DESC" }

Let’s say that we want to display the list of posts that belong to the ‘Services’ category under the posts depicted above. Since the page has already created a query object for the current category, we will have to work our way around this by manipulating the existing main query arguments.

Try our Award-Winning WordPress Hosting today!

Let’s take a look at the ways WordPress allows us to achieve this.

The query_posts Function

The query_posts() function is a way to alter the main query that WordPress uses to display posts. It does this by putting the main query to one side, and replacing it with a new query. You can see this for yourself in the wp-includes/query.php file where it is introduced.

function &query_posts($query) {
    unset($GLOBALS['wp_query']);
    $GLOBALS['wp_query'] =& new WP_Query();
    return $GLOBALS['wp_query']->query($query);
}

We will try and add another loop to display the ‘Services’ Posts by adding the following lines under the endwhile; of the current standard loop.

query_posts( array ( 'category_name' => 'services' ) );
while ( have_posts() ) : the_post();
	the_title( '<h4 class="example"></h4>' );
endwhile;

And there you go:

There is however a drawback to this method. If we try to print the category included in the query object we will notice that is altered.

Try to insert var_dump ( $wp_query->query_vars["category_name"] ); after the Services query and refresh the page. You should see this result:

string(8) "services"

This happened because the query was altered but never reverted. This approach can cause a lot of issues with the content that follow our queries.

To avoid these conflicts and clean up after a call to query_posts, make a call to wp_reset_query(), and the original main query will be restored. So your code should look like this:

query_posts( array ( 'category_name' => 'services' ) );
while ( have_posts() ) : the_post();
	the_title( '<h4 class="example"></h4>' );
endwhile;
wp_reset_query();
var_dump ( $wp_query->query_vars["category_name"] );

Upon refreshing your page you now see that the initial page category is back in the query.

string(10) "category-1"

Lastly, we ought to mention that query_posts should be avoided as it adds overhead to your query as it actually causes the main query to run again.

The get_posts() Function

The same result can be accomplished with the use of the get_posts() function to retrieves an array of posts matching the given criteria.

Replace the Services loop shown below…

query_posts( array ( 'category_name' => 'services' ) );
while ( have_posts() ) : the_post();
	the_title( '<h4 class="example"></h4>' );
endwhile;
wp_reset_query();
var_dump ( $wp_query->query_vars["category_name"] );

…with this one:

$my_query = get_posts( array ( 'category' => 17 ) );
foreach($my_query as $post) : setup_postdata($post);
	the_title( '<h4 class="example"></h4>' );
endforeach;
var_dump ( $wp_query->query_vars["category_name"] );

As you can see this time we had to work with the category id instead of the category name, according to the get_posts() arguments defined by WordPress.

Another important thing to note here is that even though we did not reset the query, the var_dump output of the query category was still intact and not changed to ‘services’. That is because get_posts() by itself does not alter the main query. We just used a new variable ‘$my_query‘ to create a new instance of the query object without replacing it.

The get_posts() function uses the same parameters as query_posts() and is suggested for use when you want to add static custom loops anywhere in your template as it is safe and easy to use.

Create a New WP_Query Object

$wp_query is an object of the WP_Query class and retrieves the necessary database content for the current page. Overriding this class is our way to customize the results and display different content.

This is the piece of code we will use:

$services_query = new WP_Query( 'category_name=services' );
if ( $services_query->have_posts() ) {
	while ( $services_query->have_posts() ) : $services_query->the_post();
        the_title( '<h4 class="example"></h4>' );
	endwhile;
}
wp_reset_postdata();
var_dump ( $wp_query->query_vars["category_name"] );

We used the ‘category_name’ argument to define the slug of the category we want its posts to be displayed.

We saved the new object new WP_Query( 'category_name=services' ); in the $services_query variable.

Then we used a while loop to display the contents and after this we reset the loop with the wp_reset_postdata(); in order to restore to original data of the main query.

If you want to experiment with other arguments of the WP_Query WordPress Class you can find the full list on its WordPress Codex page.

Start Your 14 Day Free Trial

Try our award winning WordPress Hosting!

OUR READERS ALSO VIEWED:

See how Pressidium can help you scale
your business with ease.