Sort Posts on Multiple Custom Fields

This code is in response to a request to sort posts on 6 Custom Fields, but the approach should apply to any number of Custom Fields.

Adding 6 joins to the query would result in significant overhead to the query, so an alternate method was developed which adds no additional joins or queries.

The meta_keys are named in an array. The first key is used to select posts, so all posts must have at least this Custom Field.

<?php
// Test sorting on 6 meta_keys
//
// Strategy is to get all posts with the custom_meta_artist key
// then loop through the posts getting all other keys and building an array
// with the ID and sort key.  Then sort the full posts on the ID array.
//
$meta_keys = array('artist','album','size','color','label','pressing');
$args = array(
   'meta_key' => $meta_keys[0],
   'orderby' => 'meta_value',
   'posts_per_page' => -1,
   'ignore_sticky_posts' => 1,
   );
$my_query = new WP_Query($args);

// print_r($my_query);

if( $my_query->have_posts() ) :
   foreach ( $my_query->posts as $one_post ) {
      $id = $one_post->ID;
      $custom_meta = get_post_custom($id);
      $id_array["$id"] = array();
      $id_array["$id"]['sort_key'] = '';
      foreach ( $meta_keys as $key ) {
         $id_array["$id"]['sort_key'] .= $custom_meta[$key][0] . ' ';
         $id_array["$id"][$key] = $custom_meta[$key][0];
      }
   }

   //print_r('<p>ID ARRAY:');print_r($id_array);print_r('</p>');

   // Sort posts array on the $id_array sort_key.
   function sort_6_keys($a, $b) {
      global $id_array;
      // print_r("AID:{$a->ID} SORT_KEY: {$id_array[$a->ID]['sort_key']}");
      // print_r("BID:{$b->ID} SORT_KEY: {$id_array[$b->ID]['sort_key']}");

      return strcmp( $id_array[$a->ID]['sort_key'],
         $id_array[$b->ID]['sort_key'] );
   }

   // print_r('<p>BEFORE SORT:');print_r($my_query->posts);print_r('</p>');

   usort($my_query->posts, 'sort_6_keys');

   // print_r('<p>AFTER SORT:');print_r($my_query->posts);print_r('</p>');

   while ( $my_query->have_posts() ) : $my_query->the_post();
      $id = $post->ID;
      $artist = $id_array[$id][$meta_keys[0]];
      $album = $id_array[$id][$meta_keys[1]];
      $size = $id_array[$id][$meta_keys[2]];
      $color = $id_array[$id][$meta_keys[3]];
      $label = $id_array[$id][$meta_keys[4]];
      $pressing = $id_array[$id][$meta_keys[5]];
      echo "$artist | $album | $size | $color | $label | $pressing <br />";
   endwhile;

else :
   // Code for no posts
endif;

?>

2 Responses to Sort Posts on Multiple Custom Fields

  • Mac McDonald says:

    You need to change the foreach loop that builds up the sort key. I think you can change this:

       foreach ( $meta_keys as $key ) {
          $id_array["$id"]['sort_key'] .= $custom_meta[$key][0] . ' ';
          $id_array["$id"][$key] = $custom_meta[$key][0];
       }
    

    to this (untested!):

       foreach ( $meta_keys as $key ) {
          if ($key == 'album') {
             $sort_value = sprintf('%08.0d', $custom_meta[$key][0]);
          } else {
             $sort_value = $custom_meta[$key][0];
          }
    
          $id_array["$id"]['sort_key'] .= $sort_value . ' ';
          $id_array["$id"][$key] = $custom_meta[$key][0];
       }
    
  • jberg says:

    This seems to work pretty well. One question. How would I go about having it sort by the 2nd custom field using meta_value_num.

    For example if “album” was always a number. And I wanted the sort criteria (after “artist”) to be listed alphanumeric. So 10 doesn’t show up before 3 ?

    Thanks for any help.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>