Skip to content

Instantly share code, notes, and snippets.

@mcaskill
Created May 2, 2015 05:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mcaskill/d3ada21f9ff0800a6970 to your computer and use it in GitHub Desktop.
Save mcaskill/d3ada21f9ff0800a6970 to your computer and use it in GitHub Desktop.
WordPress : Sort post titles in alphabetical order, disregarding initial definite and indefinite articles
<?php
/**
* File: Sort posts in alphabetical order, ignoring initial articles.
*
* This condition is achieved using the `post_fields` and `posts_orderby`
* filters. The implementer must provide their own list of definite and
* indefinite articles for the hooks to ignore.
*
* To enable natural language sorting, pass the following Query parameter:
* `"ignore_initial_articles" => true`
*
* @link https://css-tricks.com/ignoring-the-in-wordpress-queries/
*/
namespace McAskill\WordPress\IgnoreInitialArticles;
/**
* Retrieve definite and indefinite articles used when sorting titles.
*
* Based on {@see WP_Query::get_search_stopwords()}.
*
* @return array Initial articles.
*/
function get_initial_articles()
{
static::$articles = null;
if ( is_array( $articles ) ) {
return $articles;
}
/**
* translators: This is a comma-separated list of very common initial words that
* should be excluded from sorting and filing processes, like "a", "an", and "the".
* These are usually called "articles". You should not simply translate these
* individual words into your language. Instead, look for and provide commonly
* accepted articles in your language.
*
* @link http://www.loc.gov/marc/bibliographic/bdapndxf.html
*/
$words = explode( ',', _x( 'a,an,the', 'Comma-separated list of initial articles in your language' ) );
$articles = [];
foreach ( $words as $word ) {
$word = trim( $word, "\r\n\t " );
if ( $word ) {
$articles[] = $word;
}
}
/**
* Filter articles used when parsing search terms.
*
* @param array $articles articles.
*/
$articles = apply_filters( 'wp_initial_articles', $articles );
return $articles;
}
/**
* Filter: Create a virtual table column named "title2" at the time
* of the query. Populate the table column with the titles of the
* queried posts. If the title has a matching initial article,
* it will be removed.
*
* The list of English stopwords is the approximate search engines list,
* and is translatable.
*
* @used-by Filter: "posts_fields"
*
* @param string $fields The SELECT clause of the query.
* @param WP_Query &$wp_query The WP_Query instance (passed by reference).
*
* @return string $fields
*/
function create_temp_column( $fields, $wp_query )
{
if ( $wp_query->get( 'ignore_initial_articles', false ) ) {
global $wpdb;
$matches = get_initial_articles();
if ( count( $matches ) ) {
$matches = implode( '|', $matches );
$has_article = " 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_article ) {
$fields .= ( preg_match( '/^(\s+)?,/', $has_article ) ? $has_article : ", $has_article" );
}
}
}
return $fields;
}
/**
* Filter: Create a new custom "orderby". It orders the posts by
* the virtual table column "title2" using the ASC order.
*
* @used-by Filter: "posts_orderby"
*
* @param string $orderby The ORDER BY clause of the query.
* @param WP_Query &$wp_query The WP_Query instance (passed by reference).
*
* @return string $orderby
*/
function sort_by_temp_column( $orderby, $wp_query )
{
if ( $wp_query->get( 'ignore_initial_articles', false ) ) {
$custom_orderby = ' UPPER( title2 ) ASC';
if ( $custom_orderby ) {
$orderby = $custom_orderby;
}
}
return $orderby;
}
add_filter( 'posts_fields', __NAMESPACE__ . '\\create_temp_column' );
add_filter( 'posts_orderby', __NAMESPACE__ . '\\sort_by_temp_column' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment