1

I have a custom post type in my plugin named members, along with two custom fields; first_name and last_name. I am trying to order my listing my the last name, then the first name. The only solution I have found is one that utilizes a function to pull in the first two query meta fields and replaces them (I followed a tutorial at dotnordic.se). However, whenever I try this I have no posts returned. I have posted my code below. Any help on fixing the problem I have with my code or suggesting another way is greatly appreciated.

  // Shortcode Function
  function mba_members_list_code() {

  // WP_Query arguments
  $args = array (
  'post_type'       => 'member_data',
  'post_status'     => 'active',
  'fields'          => 'ids',
  'posts_per_page'  => -1,
  'meta_key'        => 'last_name',
  'orderby'         => 'meta_value',
  'order'           => 'ASC',
  'meta_query'      => array(
    array(
      'key'         => 'first_name',
    ),
    array(
      'key'         => 'last_name',
    ),
    array(
      'key'         => 'publicly_listed',
      'value'       => 'true',
    ),
  ),
);

// The Query
$posts = get_posts( $args );

// The Loop
if( $posts ) {
   echo "Total Members Found: " . count($posts) . "<br>";

   foreach ( $posts as $post ) {
      $q = get_post_meta($post);
      echo "<b>" . $q['first_name'][0] . " " . $q['last_name'][0] . "</b><br>";
      echo $q['city'][0] . ", " . $q['state'][0] . " " . $q['zip'][0] . "<br>";
      echo "<a href='mailto:" . $q['email'][0] . "'>" . $q['email'][0] . "</a><br>";
      echo "<br>"; 
   }
   unset( $post );       
}else{
  echo "<b>Sorry, we found no active members. Please be sure to check back soon.</b>";
}
}

EDIT

function customorder($orderby) {
global $wpdb;
return ' {$wpdb->postmeta}.meta_value, mt1.meta_value';
//return str_replace('menu_order', 'mt1.meta_value, mt2.meta_value', $orderby);
}

I know that there are other questions on this with an answer to implement this same function; however, whenever I implement it, I have no records/posts returned.

4
  • Check this link. This feature is introduced in WP4. Commented Oct 27, 2014 at 3:23
  • @Nilambar I have read over this posting before. If I was only dealing with one custom value I believe this could work, but dealing with two custom values I have my doubts. I would have to have Orderby meta_value ASC, meta_value ASC, and then set two meta keys. Please correct me if I am wrong in my thinking.
    – Michael
    Commented Oct 28, 2014 at 15:16
  • What is post_status=active Commented Oct 30, 2014 at 9:22
  • This is a custom post status that is registered for my custom post type member_data.
    – Michael
    Commented Oct 30, 2014 at 11:47

2 Answers 2

0

EDIT (Please see the original answer for full explanation of the code)

The code in my original answer works as expected, but triggers a known bug with usort (Check the bug report here #50688)

WARNING Error: [2] usort(): Array was modified by the user comparison function

This bug is triggered only when the fields parameter is set in the arguments in get_posts, so the only workable workaround I could find was to remove the fields parameter and go with full posts being retrieved.

Here is the bug free working version

<?php
$args = array (
  'post_type'              => 'member_data',
  'post_status'            => 'active',
  'posts_per_page'         => -1,
  'meta_query'             => array(
    array(
      'key'       => 'first_name',
    ),
    array(
      'key'       => 'last_name',
    ),
    array(
      'key'       => 'publicly_listed',
      'value'     => 'true',
    ),
  ),
);

$posts = get_posts( $args );

usort( $posts, function( $a, $b ){

    // sort first by last name
    $compare = strnatcmp(get_post_meta($a->ID, 'last_name', true), get_post_meta($b->ID, 'last_name', true));

    // if last names are identical, sort by first name
    if(!$compare) {
        return strnatcmp(get_post_meta($a->ID, 'first_name', true), get_post_meta($b->ID, 'first_name', true));
    }else{
        return $compare;
    }

});

if( $posts ) {
   echo "Total Members Found: " . count($posts) . "<br>";

   foreach ( $posts as $post ) {
      $q = get_post_meta($post->ID);
      echo "<b>" . $q['first_name'][0] . " " . $q['last_name'][0] . "</b><br>";
      echo $q['city'][0] . ", " . $q['state'][0] . " " . $q['zip'][0] . "<br>";
      echo "<a href='mailto:" . $q['email'][0] . "'>" . $q['email'][0] . "</a><br>";
      echo "<br>"; 
   }
   unset( $post );       
}else{
  echo "<b>Sorry, we found no active members. Please be sure to check back soon.</b>";
}
}

ORIGINAL ANSWER

Unfortunately there is no way to do this type of sorting natively, so you will need to take a different approach here.

  • Use WP_Query, or even better get_posts retrieve your posts as normal. There is no need to apply any sorting at this stage. In my personal opinion, this is useless as you can sort only by one meta_value and not two. I would rather just use php sorting to order the posts as needed. (If you are going to use pagination, rather use WP_Query).

  • You are going to use usort to sort your posts, first by last_name, then if two or more persons share the same last_name, we will sort according to first_name.

Here is the concept:

Before I start, I want to point out a few things

  • Your meta_query is a mess unfortunately. Everything is within an array within an array within an array, where as it should be 3 separate arrays within one array

  • I would rather use get_posts here and also only retrieve the post ID's as you aren't going to make use of any postdata or any of the other data returned by WP_Query

  • This is quite a heavy operation as the db is visited two times for each post. You will have to look to make use of transients here.

Now for the solution

As said use get_posts to construct your query with. We are going to retrieve the posts which matches all three meta_key's

$args = array (
  'post_type'              => 'member_data',
  'fields'                 => 'ids',
  'post_status'            => 'active',
  'posts_per_page'         => -1,
  'meta_query'             => array(
    array(
      'key'       => 'first_name',
    ),
    array(
      'key'       => 'last_name',
    ),
    array(
      'key'       => 'publicly_listed',
      'value'     => 'true',
    ),
  ),
);

$posts = get_posts( $args );

We are now going to sort $posts by last_name then first_name using usort

usort( $posts, function( $a, $b ){

    // sort first by last name
    $compare = strnatcmp(get_post_meta($a, 'last_name', true), get_post_meta($b, 'last_name', true));

    // if last names are identical, sort by first name
    if(!$compare) {
        return strnatcmp(get_post_meta($a, 'first_name', true), get_post_meta($b, 'first_name', true));
    }else{
        return $compare;
    }

});

$posts should now be sorted alphabetically by last_name, and if the last_name is the same for two or more persons, they will be sorted in alphabetical order by first_name.

Your loop will now follow and look like this

if( $posts ) {
   echo "Total Members Found: " . count($posts) . "<br>";

   foreach ( $posts as $post ) {
      $q = get_post_meta($post);
      echo "<b>" . $q['first_name'][0] . " " . $q['last_name'][0] . "</b><br>";
      echo $q['city'][0] . ", " . $q['state'][0] . " " . $q['zip'][0] . "<br>";
      echo "<a href='mailto:" . $q['email'][0] . "'>" . $q['email'][0] . "</a><br>";
      echo "<br>"; 
   }
   unset( $post );       
}else{
  echo "<b>Sorry, we found no active members. Please be sure to check back soon.</b>";
}
}
10
  • It still doesn't seem to be working correctly. It sorting last name correctly, but when that last name is the same it just adds the newest record to the bottom the those names. You can actually see my demo at s2m.demo.accessdrive.net/test-shortcode.
    – Michael
    Commented Nov 1, 2014 at 15:22
  • Are you using my code as-is. I've retested, and I can't seem replicate your problem. On my side everything works as expected. It does seem that you are making use of a shortcode, so if so, please edit your question and add your complete shortcode to your question. Commented Nov 1, 2014 at 18:01
  • I basically copied-and-pasted your code. I have updated the updated short code in my question.
    – Michael
    Commented Nov 1, 2014 at 19:35
  • Just one question, the page where you are using the shortcode, are there any custom queries on that page or any other shortcodes using a custom query. If so, all all of them resetted with WP_reset_postdat(); Commented Nov 1, 2014 at 19:40
  • No, there are no other queries on this page.
    – Michael
    Commented Nov 1, 2014 at 20:43
0

Try something like this from another post:

query_posts(
    array( 
        'post_type'      => 'custompost', 
        'posts_per_page' => 4, 
        'meta_key'       => 'mydate', 
        'orderby'        => 'meta_value', 
        'order'          => 'ASC', 
        'meta_query'     => array( 
            array( 
                'key'        => 'mydate', 
                'meta-value' => $value, 
                'value'      => $today, 
                'compare'    => '>=', 
                'type'       => 'CHAR' 
            ) 
        ) 
    )
);

Although you only need to sort by surname as the first name will be found in the loop in order of which you call it.

3
  • This would still only sort by one value, correct? I have tried this code before, but it still won't put the names in the correct/expected order.
    – Michael
    Commented Oct 27, 2014 at 2:29
  • Yes thats correct but surname and first name are linked to one person so unless do you mean sort either by surname or first ? Commented Oct 27, 2014 at 7:06
  • They are linked to one, but if I don't sort by both last name, the first name my results comes like: Charles Adams, Ashley Adams.
    – Michael
    Commented Oct 28, 2014 at 12:40

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.