Question

I have a custom post type (speaker) and I would like to load the list of speakers sorted by last name. I can't seem to figure it out. I tried the code from this post: https://stackoverflow.com/questions/16416217/wordpress-orderby-last-word-in-title

but it didn't seem to work.

add_action( 'init', 'create_post_type' );
function create_post_type() {
  register_post_type( 'event',
    array(
      'labels' => array(
        'name' => __( 'Conferences' ),
        'singular_name' => __( 'Conference' )
      ),
      'public' => true,
      'has_archive' => true,
    'supports' => array('title','editor','thumbnail'),
    )
  );
  register_post_type( 'speaker',
    array(
      'labels' => array(
        'name' => __( 'Speakers' ),
        'singular_name' => __( 'Speaker' )
      ),
      'public' => true,
      'has_archive' => true,
    'supports' => array('title','editor','thumbnail'),
    )
  );
  register_post_type( 'sponsor',
    array(
      'labels' => array(
        'name' => __( 'Sponsors' ),
        'singular_name' => __( 'Sponsor' )
      ),
      'public' => true,
      'has_archive' => true,
    'supports' => array('title','editor','thumbnail'),
    )
  );
  register_post_type( 'venue',
    array(
      'labels' => array(
        'name' => __( 'Venues' ),
        'singular_name' => __( 'Venue' )
      ),
      'public' => true,
      'has_archive' => true,
    'supports' => array('title','editor','thumbnail'),
    )
  );
  register_post_type( 'session',
    array(
      'labels' => array(
        'name' => __( 'Sessions' ),
        'singular_name' => __( 'Session' )
      ),
      'public' => true,
      'has_archive' => true,
    'supports' => array('title','editor','thumbnail'),
    )
  );
}

The code i'm calling the custom post type is:

<?php 
// args  
$args = array(
    'numberposts'   => -1,
    'post_type'     => 'speaker',
    'meta_key'      => 'speaker-front-page',
    'meta_value'    => '1',
    'orderby' => 'speaker_last_name',
    'order'   => 'ASC'
);
// query
add_filter( 'posts_orderby' , 'posts_orderby_lastname' );
$the_query = new WP_Query( $args );
?>
<?php if( $the_query->have_posts() ): ?> <div id="speakerrow"><h1>SPEAKERS<h1><hr>
    <?php while( $the_query->have_posts() ) : $the_query->the_post(); 
echo '<div class="flex_column av_one_third flex_column_div">';
echo do_shortcode("[av_image src='". get_field('speaker-photo')."' attachment='' attachment_size='full' align='center' animation='pop-up' styling='' hover='av-hover-grow' link='".get_the_permalink()."' target='' caption='' font_size='' appearance='' overlay_opacity='0.4' overlay_color='#000000' overlay_text_color='#ffffff'][/av_image]" );
echo do_shortcode("[av_heading tag='h2' padding='10' heading='". get_the_title()."' color='' style='blockquote modern-quote modern-centered' subheading_active='subheading_below' subheading_size='15']". get_field('speaker-company')."[/av_heading]");
echo '</div>';
?>

the best i could think was to add a custom metafield called last name and am sorting by that, I like not having to type the speakers last name 2 times if possible.

how do i add the order by field to sort by the 2nd (and last) word in the post title?

Was it helpful?

Solution

Order by the last word in the post title

To order by the speaker's last name, you can use the following setup (PHP 5.4+):

// args  
$args = [
    'posts_per_page'   => 10,
    'post_type'        => 'speaker',
    'meta_key'         => 'speaker-front-page',
    'meta_value'       => '1',
    'orderby'          => 'wpse_last_word',          //<-- Our custom ordering!
    'order'            => 'ASC'
];

// query
$the_query = new WP_Query( $args );

where the 'wpse_last_word' input is supported by the following:

/**
 * Order posts by the last word in the post_title. 
 * Activated when orderby is 'wpse_last_word' 
 * @link https://wordpress.stackexchange.com/a/198624/26350
 */
add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    if( 'wpse_last_word' === $q->get( 'orderby' ) && $get_order =  $q->get( 'order' ) )
    {
        if( in_array( strtoupper( $get_order ), ['ASC', 'DESC'] ) )
        {
            global $wpdb;
            $orderby = " SUBSTRING_INDEX( {$wpdb->posts}.post_title, ' ', -1 ) " . $get_order;
        }
    }
    return $orderby;
}, PHP_INT_MAX, 2 );

This is based on my answer here on ordering terms by the last word.

OTHER TIPS

The accepted answer is pretty brittle and won't be able to handle the many variations of the order by parameters.

Here's a filter that should be a little more robust:

/**
 * Order posts by the last word in the post_title.
 * Activated when orderby is 'wpse_last_word'
 * @link http://wordpress.stackexchange.com/a/198624/26350
 */
add_filter('posts_orderby', function($orderby_sql, \WP_Query $q) {
  $orderbys = $q->get('orderby');
  if (!$orderbys) {
    return;
  }
  if ($orderby_sql) {
    $orderby_sql_array = [$orderby_sql];
  }
  else {
    $orderby_sql_array = [];
  }
  if (!is_array($orderbys)) {
    $words = explode(' ', $orderbys);
    $orderbys = [];
    foreach ($words as $word) {
      $orderbys[$word] = $q->get('order');
    }
  }
  global $wpdb;
  foreach ($orderbys as $orderby => $direction) {
    if ($orderby == 'wpse_last_word') {
      if (!$direction || !in_array(strtoupper($direction), ['ASC', 'DESC'])) {
        $direction = 'DESC';
      }
      $orderby_sql_array[] = "SUBSTRING_INDEX({$wpdb->posts}.post_title, ' ', -1) $direction";
    }
  }
  return implode(', ', $orderby_sql_array);
}, 100, 2);

The basic usage is (but it will accept more complex variations of the order by parameters):

$args = [
    'posts_per_page'   => 10,
    'post_type'        => 'speaker',
    'meta_key'         => 'speaker-front-page',
    'meta_value'       => '1',
    'orderby'          => 'wpse_last_word',
    'order'            => 'ASC'
];

$the_query = new WP_Query( $args );

The accepted answer worked fine except it messed up the ordering of Scandic (finnish / swedish…) characters. The letters Å Ä Ö should be at the end.

Here's how it works with Scandic alphabet.

add_filter( 'posts_orderby',
    function( $orderby, \WP_Query $q )
    {
        if ( 'wpse_last_word' === $q->get( 'orderby' ) ) {
            $get_order = $q->get( 'order' );
            if ( in_array( strtoupper( $get_order ), [ 'ASC', 'DESC' ], true ) ) {
                global $wpdb;
                $orderby = " SUBSTRING_INDEX( {$wpdb->posts}.post_title, ' ', -1 ) COLLATE utf8mb4_swedish_ci " . $get_order . ", SUBSTRING_INDEX( {$wpdb->posts}.post_title, ' ', 1 ) COLLATE utf8mb4_swedish_ci " . $get_order . ';';
            }
        }
        return $orderby;
    },
    PHP_INT_MAX,
2 );
Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top