Support

Account

Home Forums Front-end Issues Sort by one field, and then by another Reply To: Sort by one field, and then by another

  • Look at https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters and look for ‘orderby’ with multiple ‘meta_key’s and also here https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/

    This should probably be done with a pre_get_posts filter to alter the main query. This is an example using what you’ve posted to give an example.

    
    $args = array(
      'numberposts' => -1,
      'post_type' => 'locations',
      'meta_query' => array(
        'state_clause' => array(
          'key' => 'state',
          'compare' => 'EXISTS',
        ),
        'city_clause' => array(
          'key' => 'city',
          'compare' => 'EXISTS',
        ),
      ),
      'orderby' => array(
        'state_clause' => 'ASC',
        'city_clause' => 'ASC',
        'title' => 'ASC'
      )
    );
    $the_query = new WP_Query($args);
    

    The above will order all of the posts so that they end up being grouped together by state and then city. The next step is that as you’re looping through the posts you need to look at what the state/city of the post is and compare it to the previous post. This can be a little confusing because of the circular logic of possibly outputting closing tag for elements before outputting the matching opening tags for those elements.

    
    if ($the_query->have_posts()) {
      // variables to hold values from previous post
      $last_state = '';
      $last_city = '';
      
      while ($the_query->have_posts()) {
        // get state and compare it to the previous post
        $this_state = get_field('state');
        if ($this_state != $last_state) {
          // the state has changed
          if ($last_state != '') {
            // close the post UL, city DIV, and state DIV that we'll open next
            // this will be skipped if this is the first post
            // because $last_state will be empty
            echo '</ul></div><!-- .city --></div><!-- .state -->';
          }
          echo '<div class="state"><h2>'.$this_state.'</h2>';
          // clear $last_city
          $last_city = '';
          // set $last_state to $this_state
          $last_state = $this_state;
        } // end if new state
        // get the city and compare to previous post
        $this_city = get_field('city');
        if ($this_city != $last_city) {
          // the city has changed
          if ($last_city != '') {
            // close the post UL, city DIV we'll open next
            // this will be skipped if this is the first post for city
            // because $last_city will be empty
            echo '</ul></div><!-- .city -->';
          }
          echo '<div class="city"><h3>'.$this_city.'</h3><ul>';
          // set $last_city to $this_city
          $last_city = $this_city;
        } // end if new city
        ?>
          <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
        <?php 
      } // end while have posts
      // we need to close the last set of elements that were opened
      echo '</ul></div><!-- .city --></div><!-- .state -->';
    } // end if have_posts