5 Ways to Alter Built-In WordPress Queries Using Pre_Get_Posts

By Andrew Gehman post-attributes

WordPress has many built-in queries that run automatically on archive pages. WordPress developers sometimes need a way to change these queries to better meet build requirements when developing a site. In such cases, the pre_get_posts hook is a very handy tool. Here are a few ways to alter built-in WordPress queries using pre_get_posts:

What is pre_get_posts and What Does it do?

Pre_get_posts is an action hook that is called before the query is run. This allows developers to alter existing WordPress queries before they are executed and thus change what is returned from those queries.

When to Use pre_get_posts

In many cases, it may be more ideal to write your own query from scratch using WP_Query. However, pre_get_posts is the ideal way to alter existing queries in WordPress. Examples of existing WordPress queries would be the ones you’d find on the main blog page or on any archive page. 

How to Use pre_get_posts

The pre_get_posts action hook is used in the functions.php file. The basic structure of the code is below:

function minddnd_alter_your_query( $query ) {

   // Use a conditional to determine what/where you need to alter the query
   // Code to alter the query goes here
}

add_action( 'pre_get_posts', 'minddnd_alter_your_query' );

In most cases, we won’t want to alter any queries in the admin. We can prevent the Admin from being affected by using the is_admin() function to check if we are in the admin, and if so, simply returning the original query as intended. This would look something like this:

function minddnd_alter_your_query( $query ) {

  // don't modify admin queries
  if( is_admin() ) {
    return $query; 
  }
}

add_action( 'pre_get_posts', 'minddnd_alter_your_query' );

Prerequisites Before You Begin

Stuff to Know

A couple of the examples in this post assume you are familiar with the popular Advanced Custom Fields WordPress Plugin. If not, ACF has some pretty great documentation to help get you up to speed.

Make a Backup

We’ll also be working in your theme’s functions.php file. An error in the functions.php file can disable your entire site, so we recommend backing up your site, or at least making a backup copy of functions.php, before making any changes.

WordPress Conditionals

The pre_get_posts action affects all queries and, in most cases, you’ll only want to change specific queries. WordPress has a slew of built-in Conditional Tags that are very useful in targeting the appropriate query. Most of the time, you’ll probably want to target the main query on a page. In those instances, it is recommended that you use the $query->is_main_query() conditional in tandem with one of the other WordPress conditionals to alter the desired pages. 

5 Ways to Alter Built-In WordPress Queries Using Pre_Get_Posts

Now that we’ve looked into how pre_get_posts works, let’s look at some cool things we can do with it. Many of the examples below can be adjusted to fit specific needs just by changing the conditionals. 

1. Reverse the Post Order on WordPress Archive Pages

By default, all WordPress archive pages display posts in reverse chronological order based on the time and date they were published. This means that the most recently published post will appear first and the oldest post will appear last. In certain situations, you may want to display your posts in chronological order with the oldest post appearing first. 

We can reverse the post order by simply changing the query parameter ‘order’ to ‘ASC.’ The code below will reverse the order of any archive on the site. If you want to target a specific archive, such as an archive for a custom post type called ‘projects,’ you would substitute is_archive for is_post_type_archive( ‘projects’ ). If you wanted to reverse the order of the posts on your main blog archive page, you would use is_home().

function minddnd_reverse_order_archive( $query ) {


   if( is_admin() ) {
     return $query;
   }

   if ( is_archive() && $query->is_main_query() ) {
     $query->set( 'order', 'ASC' );
   }
}


add_action( 'pre_get_posts', 'minddnd_reverse_order_archive' );

2. Display the Order of Blog Posts Alphabetically by Title

This example is pretty simple and straightforward. To display blog posts in alphabetical order, set ‘orderby’ to ‘title’ and the ‘order’ to ‘ASC’ in our function. Use the is_home() function in conjunction with $query->is_main_query() to only alter the results on the main blog page.

function mind_query_order_by_title( $query ) {

  if ( is_admin() ) { 
    return; 
  }
 if ( is_home() && $query->is_main_query() ) {
   $query->set( 'orderby', 'title' );
   $query->set( 'order', 'ASC' );
 }

}
add_action( 'pre_get_posts', mind_query_order_by_title );

3. Display the Order of Posts by Custom Date Rather Than Published Date

In a previous post, we looked in detail at how to order the WordPress archive templates by ACF Date Picker field. This particular technique requires the use of the popular Advanced Custom Fields Plugin for WordPress. With the Advanced Custom Fields Date Picker Field, you can define a date for your post other than the published date.

In that earlier post, we looked at an example using a custom post type of “Events.” In that scenario, we would always want the next upcoming event showing up first. We established an “Event Date” with a field name of ‘event_date’ using the ACF Date Picker. Rather than use a conditional to check if we were on a particular archive or page, we simply checked if the post type was ‘event.’  This forced all Events to appear in chronological order, based on the event date, regardless of where they appeared on the site.  

function mind_pre_get_posts( $query ) {
  
  if( is_admin() ) {
    return $query; 
  }
  if( isset($query->query_vars['post_type']) && $query->query_vars['post_type'] == 'event' && $query->is_main_query() ) {
    
    $query->set('orderby', 'meta_value'); 
    $query->set('meta_key', 'event_date');   
    $query->set('order', 'ASC'); 
    
  }

}
add_action('pre_get_posts', 'mind_pre_get_posts');

For more details on how we set everything up, check out how to order the WordPress Archive Templates by ACF Date Picker field.

4. Hide Protected Posts

When you publish a new post in WordPress, you have the option of setting the post’s visibility as “Public,” “Password Protected,” or “Private.” While a “Public” post is visible to all visitors, a “Private” post is only accessible by logged in users and a “Password Protected” post requires a visitor to log in with a password to view the post. The title of Password Protected posts is shown by default with “Protected: ” prepended to the title, and will show up in the posts page and any archive files.

If you’d prefer to hide those protected posts from users, doing so is easy with pre_get_posts. We need to be sure we are not removing the individual posts page so we add ! $query->is_singular() to the conditional statement. 

function  minddnd_exclude_password_protected_posts( $query ) {

   if( is_admin() ) { 
    return $query; 
   }

   if ( !$query->is_singular() && $query->is_main_query() ) {
       $query->set( 'has_password', false );
   }

}

add_action( 'pre_get_posts', 'minddnd_exclude_password_protected_posts' );

5. Manually Order Custom Post Types on Archives Pages

In some situations, you may want to display your posts in a completely custom order. For example, if you have a custom post type of “Services,” you may want to order those services by most popular, or perhaps you want to position the most important services first.

First, we’ll need to define or establish a way that we can link a number to each post. Then we can alter our query to order the posts by that number. Let’s look at a couple of ways we can do this.

Order

Order is one of the Page Attributes that is only enabled on WordPress Pages by default and found on the right sidebar of the admin area. You can also enable the feature for custom post types. This field was originally created to allow WordPress users to define the order that pages would appear in the menu by changing the order number. Now in WordPress, we typically use navigation menus in place of this feature, but we can still use Order as a way to assign a number to our custom posts or pages. All pages will have a default order of zero.

Adding Order to Custom Post Types

When you register a new post type in WordPress, you pass an array of arguments to the register_post_type function that defines the functionality of your custom post type. To enable “Order” on custom post types, you need to include ”page-attributes’ in the ‘supports’ array that you pass to register_post_type. Below is a code example for a services post type that supports ‘page-attributes’:

$service_args = array(
  'public' => true,
  'has_archive' => true,
  'label'  => 'Services',
  'supports' => array('title','editor','thumbnail','revisions','author', 'page-attributes')
);

register_post_type( 'services', $service_args );

Once you have Page Attributes (or in the case of a Custom Post Type, they will be titled ‘Post Attributes’) enabled, you can manually give each post a unique Order number. Note in the example below that the actual value for ‘orderby’ is ‘menu_order’:

function minddnd_order_services( $query ) {

  if ( is_admin() ) {
    return;
  }

  if ( is_post_type_archive( 'services') && $query->is_main_query()  ) {
    $query->set( 'order' , 'asc' );
    $query->set( 'orderby', 'menu_order');
  }
}
add_action( 'pre_get_posts', 'minddnd_order_services'  );

ACF Number Field

We could also use an ACF Number field to assign a number to our posts. This process works essentially the same way as our previous example. The only difference is we’ll sort by the ACF Number field rather than ‘menu_order’.

First, we’ll create an ACF Number field for our Services post type with the field name of “post_order.” Then, we can use this field to manually assign each of our posts a number.

In the code example below, we’ve set ‘orderby’ to ‘meta_value’, the ‘meta_key’ to ‘post_order’ (our ACF Number field name), and the ‘order’ to ‘ASC’. The resulting output will display our posts sorted by the “post_order” from lowest to highest.

Note: If you are using this method, be sure every post has a value set for this field. Otherwise, any post without a value won’t show up in the archive.

function minddnd_order_services( $query ) { 

  if ( is_admin() ) { 
    return; 
  } 

  if ( is_post_type_archive( 'services') && $query->is_main_query() ) { 
    $query->set('orderby', 'meta_value'); 
    $query->set('meta_key', 'post_order');   
    $query->set('order', 'ASC');
  } 
} 

add_action( 'pre_get_posts', 'minddnd_order_services' );

Hopefully, you can see from the examples above that pre_get_posts is a powerful and handy tool for WordPress Developers looking to easily alter built-in WordPress queries.

View Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Are You Ready To Do More with Mind?

Click the link below to get started!

Work With Mind