Is “orderby” in WP Meta Query conflicting with Meta Query?

WordPress standard query sucks with multiple meta queries when orderby is set to meta key.

After a lot of headaches and time lost I wrote a custom class that allow me to better handle that sort of things. Here there is a simplified version of my class, but it hould work for your purposes:

class EventQuery {

  private $args;
  private $order;
  private $query;

  static function query( Array $args, \WP_Query $query = NULL ) {
    if ( is_null( $query ) ) {
      $query = new \WP_Query;
    }
    $class = __CLASS__;
    $eventquery = new $class( $args, $query );
    return $eventquery->enable()->run()->disable()->getQuery();
  }

  function __construct( Array $args, \WP_Query $query ) {
    $this->args = $args;
    $this->order = isset( $args['order'] ) && strtoupper( $args['order'] === 'DESC' )
      ? 'DESC'
      : 'ASC';
    $this->query = $query;
  }

  function enable() {
    add_filter( 'posts_fields_request',  array( $this, 'filterFiels' ) );
    add_filter( 'posts_join_request',    array( $this, 'filterJoin'  ) );
    add_filter( 'posts_where_request',   array( $this, 'filterWhere' ) );
    add_filter( 'posts_orderby_request', array( $this, 'filterOrder' ) );
    return $this;
  }

  function disable() {
    remove_filter( 'posts_fields_request',  array( $this, 'filterFiels' ) );
    remove_filter( 'posts_join_request',    array( $this, 'filterJoin'  ) );
    remove_filter( 'posts_where_request',   array( $this, 'filterWhere' ) );
    remove_filter( 'posts_orderby_request', array( $this, 'filterOrder' ) );
    return $this;
  }


  function filterFiels( $sql ) {
    return $sql . ", metastartdate.meta_value as start_date";
  }

  function filterJoin( $sql ) {
    global $wpdb;
    return $sql . " INNER JOIN {$wpdb->postmeta} 
        AS metastartdate ON ({$wpdb->posts}.ID = metastartdate.post_id)";
  }

  function filterWhere( $sql ) {
    return $sql . " AND metastartdate.meta_key = 'event_start_date'";
  }

  function filterOrder( $sql ) {
    return "start_date {$this->order}";
  }

  function run() {
    $this->query->query( $this->args );
    return $this;
  }

  function getQuery() {
    return $this->query;
  }

}

It can be used like so:

$args = array (
  'post_type' => 'post',
  'posts_per_page' => 10,
  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key' => 'event_start_date',
      'value' => array( $date_1, $date_2 ),
      'type' => 'numeric',
      'compare' => 'BETWEEN'
    ),
    array(
      'key' => 'event_end_date',
      'value' => array( $date_1, $date_2 ),
      'type' => 'numeric',
      'compare' => 'BETWEEN'
    ),
  ),
  'orderby' => 'custom', // <-- NOTE THIS 
  'order' => 'ASC'   // <-- AND THIS 
);

$query = EventQuery::query( $args ); // return a WP_Query

if ( $query->have_posts( ) ) {
  // loop here
}

Of course you can use ‘DESC’ to order by desc start date.

Leave a Comment