Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have a People post type with a Person Type taxonomy (e.g. "Staff"). On the Person Type Term Archive pages, I want to sort by a "Sort Name" custom field. To do so, I'm using a pre_get_posts call:

<?php
function my_query_tweaks( $query ) {

    // stop immediately if we're in the admin or not working with the main query
    if( is_admin() || !$query->is_main_query() )
        return;

    // order people by sortname
    if( is_post_type_archive( 'ciswa_people' ) || is_tax( 'ciswa_person_type' ) ) {
        $query->set( 'meta_key', 'cis_sort_name' );
        $query->set( 'orderby', 'meta_value' );
        $query->set( 'order', 'ASC' );
    }

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

However, when I do this, the term archive pages return no results! Commenting out the meta_key line brings up the correct posts but, of course, unordered.

When I look at the query WordPress is generating, I think the issue is that WordPress is trying to also query posts based on the meta_key rather than just ordering by it. From $wp_query on the relevant empty taxonomy term archive page:

["tax_query"]=>
  object(WP_Tax_Query)#343 (2) {
    ["queries"]=>
    array(1) {
      [0]=>
      array(5) {
        ["taxonomy"]=>
        string(17) "ciswa_person_type"
        ["terms"]=>
        array(1) {
          [0]=>
          string(5) "staff"
        }
        ["include_children"]=>
        bool(true)
        ["field"]=>
        string(4) "slug"
        ["operator"]=>
        string(2) "IN"
      }
    }
    ["relation"]=>
    string(3) "AND"
  }
  ["meta_query"]=>
  object(WP_Meta_Query)#344 (2) {
    ["queries"]=>
    array(1) {
      [0]=>
      array(1) {
        ["key"]=>
        string(13) "cis_sort_name"
      }
    }
    ["relation"]=>
    string(3) "AND"
  }

...and...

["request"]=>
  string(498) "SELECT SQL_CALC_FOUND_ROWS  fi_posts.ID FROM fi_posts  INNER JOIN fi_term_relationships ON (fi_posts.ID = fi_term_relationships.object_id) INNER JOIN fi_postmeta ON (fi_posts.ID = fi_postmeta.post_id) WHERE 1=1  AND ( fi_term_relationships.term_taxonomy_id IN (6,10) ) AND fi_posts.post_type = 'ciswa_people' AND (fi_posts.post_status = 'publish' OR fi_posts.post_status = 'private') AND (fi_postmeta.meta_key = 'cis_sort_name' ) GROUP BY fi_posts.ID ORDER BY fi_postmeta.meta_value ASC LIMIT 0, 10"
share|improve this question
2  
Do all the posts in the taxonomy have the meta key you are trying to sort by? –  helgatheviking Jul 10 '13 at 2:57
1  
This is going to be tricky, but not impossible, if you don't have that key assigned to each post. I think the solution might end up looking something like this: wordpress.stackexchange.com/a/100200/21376 –  s_ha_dum Jul 10 '13 at 3:07
    
The meta_key is required for every post but is only for alphanumeric ordering, not querying. To be clear, I have no need for a meta_query in this scenario, but pre_get_posts seems to insist on making one and I think that's what's throwing the results out of whack. –  mrwweb Jul 10 '13 at 4:59
    
@helgatheviking see my comment on the answer below. The meta_key is always on every post, so I'm confused why this isn't working. –  mrwweb Jul 10 '13 at 13:48
add comment

2 Answers

'meta_key' => 'keyname' must be part of the query to be able to sort by its value.
Unfortunately, that will immediately result in the query being limited to posts where the key is set (its value can be empty, but it must exist).

Hence, either consider the method s_ha_dum linked to in the question comments, or choose to do the sorting after the query.

Let me offer an approach to that (untested, just a concept):

function wpse105899_sort_posts_by_cis_sort_name( $a, $b ) {
    $a_cis_sort_name = get_post_meta( $a->ID, 'cis_sort_name', true );
    $b_cis_sort_name = get_post_meta( $b->ID, 'cis_sort_name', true );

    if ( $a_cis_sort_name === $b_cis_sort_name ) {
        return 0;
    } else if ( $a_cis_sort_name > $b_cis_sort_name ) {
        return 1;
    } else {
        return -1;
    }
}

usort( $query->posts, 'wpse105899_sort_posts_by_cis_sort_name' );

Note: The above format of the conditional in the usort callback serves the purpose of readability. In production, it can be a one-liner:

function wpse105899_sort_posts_by_cis_sort_name( $a, $b ) {
    $a_cis_sort_name = get_post_meta( $a->ID, 'cis_sort_name', true );
    $b_cis_sort_name = get_post_meta( $b->ID, 'cis_sort_name', true );

    return $a_cis_sort_name === $b_cis_sort_name ? 0 : ( $a_cis_sort_name > $b_cis_sort_name ) ? 1 : -1;
}
share|improve this answer
    
I'm confused because the key is always set to something (sometimes a number and other times "Lastname, First," but it's always set. Is that not what you mean when you say " limited to posts where the key is set?" If that's the case, it seems like that shouldn't be causing problems, but it obviously is. –  mrwweb Jul 10 '13 at 13:48
    
Yup - that is what I meant. I now that you clarified that it is present everywhere, I am confused as well. –  Johannes Pille Jul 10 '13 at 13:54
    
And that's the answer. (See below.) Thank you for the help. I would not have found this answer otherwise. –  mrwweb Jul 10 '13 at 13:56
add comment

Head desk

I had the meta_key wrong. @Johannes Pille's answer was the key in that it pointed me to the fact that if the key was there this shouldn't be a problem. It was there but I wasn't using it.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.