1

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"
4
  • 2
    Do all the posts in the taxonomy have the meta key you are trying to sort by? Commented Jul 10, 2013 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
    Commented Jul 10, 2013 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
    Commented Jul 10, 2013 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
    Commented Jul 10, 2013 at 13:48

2 Answers 2

1

'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;
}
3
  • 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
    Commented Jul 10, 2013 at 13:48
  • Yup - that is what I meant. I now that you clarified that it is present everywhere, I am confused as well. Commented Jul 10, 2013 at 13:54
  • And that's the answer. (See below.) Thank you for the help. I would not have found this answer otherwise.
    – mrwweb
    Commented Jul 10, 2013 at 13:56
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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