Can you order posts from wp_query by two custom meta fields?

I’m working on a site that lists events. Right now this query lists events by date:

$args = array(
                    'post_type' => 'espresso_event',
                    'post_status' => 'publish',
                    'posts_per_page' => 8,
                    'category_name' => 'homepage',
                    'meta_key' => 'event_start_date',
                    'meta_query' => array(
                        array(
                            'key' => 'event_start_date',
                            'value' => date('Y-m-d'),
                            'compare' => '>=', // compares the event_start_date against today's date so we only display events that haven't happened yet
                            'type' => 'DATE'
                            )
                        ),
                    'orderby' => 'meta_value',
                    'order' => 'ASC'
                );

        $temp = $wp_query;
        $wp_query = null;
        $wp_query = new WP_Query();
        $wp_query->query($args);

However, some events take place on the same date at different times and I need it to order posts first by date and then by a custom meta called event_start_time. Problem is, I’m at a loss on how to do that. wp_query doesn’t seem capable of ordering by two custom meta.

Read More

Is this something I can even do?

Related posts

Leave a Reply

1 comment

  1. In pure SQL you can use comma separated strings to do what you want– that is, something like ORDER BY post_type,post_name,post_date. However, WP_QUERY will not allow that kind of thing to pass through. There is a rigid set of allowed values.

    There are a couple of ways I can think of to do this.

    You can “push” query vars to WP_Query just by adding them to your $args. Caveat: I do not know if this is intended behavior. It may be a quirk in the code base.

    $args = array(
      'post_type' => 'post',
      'post_status' => 'publish',
      'posts_per_page' => 8,
      'orderby' => 'post_date,post_title',
      'order' => 'ASC',
      'myorderby' => 'post_date,post_name'
    );
    
    $temp = $wp_query;
    $wp_query = null;
    $wp_query = new WP_Query();
    $wp_query->query($args);
    
    while ($wp_query->have_posts()) {
      $wp_query->the_post();
      echo $post->post_title.'<br/>';
    }
    

    Add a filter on posts_orderby

    function posts_odb_filter_wpse_96900($odb) {
      global $wp_query;
      $myodb = $wp_query->get('myorderby');
      if (!empty($myodb)) {
        preg_match('/(ASC|DESC)$/',$odb,$dir);
        $odb = $myodb;
        if (!empty($dir[0])) {
          $odb .= ' '.$dir[0];
        }
      }
      return $odb;
    }
    add_filter('posts_orderby','posts_odb_filter_wpse_96900');
    

    … and you can order to your heart’s content.

    The (probably) more canonical way to do it is to check the query vars, or other information, and alter the query accordingly.

    function posts_odb_filter_wpse_96900_v2($odb) {
      global $wp_query;
      if (is_front_page() && !is_main_query()) { // whatever conditions you need
        preg_match('/(ASC|DESC)$/',$odb,$dir);
        $odb .= ' post_date,post_name '.$dir[0];
      }
      return $odb;
    }
    add_filter('posts_orderby','posts_odb_filter_wpse_96900_v2');
    

    The drawback of the second method is that you have to create a filter for each query that you need to alter.