How to modify a WordPress query the right way

If you’re a developer, and have worked with WordPress, you should be familiar with something called “The Loop“. The Loop is the way WordPress handles the display of the content you create.

Behind The Loop, there are the Wp_Query class, and the $wp_query object. That way WordPress decides what to output based on the page you’re seeing (really simple explanation, not intended to be technically accurate).

So, often you’ll need to alter that default query to display what you want, and there are many ways of doing it (I won’t talk about all of them), but if you are doing it in your template, please stop, read this and start over.

WordPress Actions

The Actions, or Actions Hooks, are triggered when something happens during the site load. In our case, we’ll trigger an action before the posts are get (remember we’re trying to modify the query before the posts are shown).

We’ll use then the

pre_get_posts

action.

You can create a function in your functions.php file, or inside a Plugin.

Lets say you have a custom post type called “clients”, and for that you have a custom field called “city”.

When WordPress shows the posts in “clients”, it will by default order them by the date they have been posted. But you want to order them by the “city” custom field.

Also, you want to exclude the clients under the city “Miami”.

/**
 * Modify the query
 * if the post type is client, then do not show
 * the ones with the city Miami, and order
 * the results by the city
 *
 * @param $query
 */
function PLUGIN_modify_query($query) {

  //The action also runs in the admin area, so be careful if
  //you don't want to alter the default behavior there
  if(!is_admin()) {
    if (array_key_exists('post_type', $query->query)) {
      if ($query->query['post_type'] == 'clients') {

        $meta_query = [
          [
            //you have to use here the real key of your field
            'key' => 'city',
            'value' => ['Miami'],
            'compare' => 'NOT IN'
          ],
        ];
        
        $query->set('meta_query', $meta_query);

        //Apply the order by options
        $query->set('meta_key', 'city');
        $query->set('orderby', ['meta_value' => 'ASC']);
      }
    }
  }
}

add_action( 'pre_get_posts' , 'PLUGIN_modify_query' );

This way you can modify the query without changing your template files, it’s cleaner!