Ignoring `the` in WordPress Queries

The following is a post by Jason Witt. Here Jason shares a method for doing something you might assume is pretty easy, but turns out to be a little bit more complicated than you might like. Fortunately with Jason's code and examples, it can be easy.

Recently I had to make a custom WordPress query that listed post titles in alphabetical order. I started with a basic query like this.

$my_query = new WP_Query(array(
  'post_type'  => 'post',
  'orderby'    => 'title',
  'order'      => 'ASC'
));

My results were:

The results are technically in alphabetical order, but not how I wanted. I wanted the word "the" to be ignored if it was the title started with that, and to alphabetize by the next word instead. The desired order should look like this:

The "the" is called an Initial Article. You know: "A", "An", and "The", the thing that sometimes gets put at the beginning of proper nouns. If you want to go international you can include "La" (Spanish) and "Les" (French). After a lot of searching and combing through WordPress Codex, I couldn't find a native WordPress WP_Query solution for ignoring an Initial Article.

What I did find were two very helpful filters that can let me achieve this, posts_fields and posts_orderby. With a little SQL know-how and PHP trickery. I was able to achieve the desired result.

Perhaps unsurprisingly, this is a pretty common need. Imagine a music library. Grouping all the bands that start with "The" under "T" would be needlessly confusing. Instead it's like this:

How To Do It

Let me share with you how I did it, so you can use this in any WordPress project that requires you to alphabetize a WordPress query while ignoring Initial Articles.

In your `functions.php` file, add the following two functions.

function wpcf_create_temp_column($fields) {
  global $wpdb;
  $matches = 'The';
  $has_the = " CASE 
      WHEN $wpdb->posts.post_title regexp( '^($matches)[[:space:]]' )
        THEN trim(substr($wpdb->posts.post_title from 4)) 
      ELSE $wpdb->posts.post_title 
        END AS title2";
  if ($has_the) {
    $fields .= ( preg_match( '/^(\s+)?,/', $has_the ) ) ? $has_the : ", $has_the";
  }
  return $fields;
}

function wpcf_sort_by_temp_column ($orderby) {
  $custom_orderby = " UPPER(title2) ASC";
  if ($custom_orderby) {
    $orderby = $custom_orderby;
  }
  return $orderby;
}

This first function, wpcf_create_temp_column, is going to create a virtual table column named "title2" at the time of the query. It populates the table column with the titles of your posts. If the title has the Initial Article "The", it will be removed.

The second function, wpcf_sort_by_temp_column, creates a new custom orderby. It orders the posts by the virtual table column "title2" using the ASC order.

To have these function work their magic on your queries, you simply add them to the filters I mentioned before.

add_filter('posts_fields', 'wpcf_create_temp_column');
add_filter('posts_orderby', 'wpcf_sort_by_temp_column');

Now your queries will order your post titles in alphabetical order while ignoring the Initial Article "The". If you want to add additional Initial Articles to be ignored all you have to do is include them to the $matches variable in the wpcf_create_temp_column function, and separate them with a pipe character.

$matches = 'A|An|The|La|Les';

Now let's say you have multiple WordPress queries that are being ordered by the title, but you don't want to apply these filters to those queries. By adding the filters in your `functions.php` file they will affect every query on your site, so you probably don't want that. Not to mention it may affect your site's performance by using unnecessary filters. Here's how you can apply those filters to specific queries.

First remove the add_filter functions from your `functions.php` file. Then in your template file with desired query, put the add_filter functions along with remove_filter functions like this.

add_filter('posts_fields', 'wpcf_create_temp_column'); // Add the temporary column filter
add_filter('posts_orderby', 'wpcf_sort_by_temp_column'); // Add the custom order filter

$query = new WP_Query(array('post_type' => 'post')); // Your custom query

remove_filter('posts_fields','wpcf_create_temp_column'); // Remove the temporary column filter
remove_filter('posts_orderby', 'wpcf_sort_by_temp_column'); // Remove the temporary order filter 

if (have_posts()) : while ($query->have_posts()) : $query->the_post(); // The query output

  the_title(); 
  echo '<br/>';

endwhile; endif; wp_reset_postdata();

In the above code, you're adding the filters, executing the query, and then immediately removing the filters. By wrapping the query code in the filter your isolating those filter to that specific query.

Happy sorting!