WordPress PHP – How do I sort category/archive results by a specific “meta_key” value?

I previously made a post here: How do I sort by a custom field without manually creating a new page?

However, I believe I asked the wrong question (and I may still be asking the wrong question). Actually think I may need a complex query that will display posts ordered by a meta value. The site is using a theme called “AgentPress”. I believe passing params via the URL bar may be too simplistic for what I need.

Read More

Honestly I like the way the current category pages display (formatting, etc.), I simply need to “short-circuit” the process so that any category/archive page is sorted by the meta_key for the “property price” as opposed to the date of entry. If there is a simple, more “WordPress-y” mechanism for doing this, I’m all ears. Please be explicit about where to place the code, etc.

FYI, at this point it’s clear that passing “order=ASC” and “order=DESC” in the URL works. However, it seems that nothing I do with “meta_key” or anything related has any effect.

Thanks in advance.

Related posts

Leave a Reply

3 comments

  1. You can add a filter on pre_get_posts hook.

    Put this code in functions.php (in your theme dir) :

    add_filter('pre_get_posts', 'pre_get_posts_hook' );
    
    function pre_get_posts_hook($wp_query) {
        if (is_category() || is_archive())
        {
            $wp_query->set( 'orderby', 'meta_value_num' );
            $wp_query->set( 'meta_key', 'price' );
            $wp_query->set( 'order', 'ASC' );
            return $wp_query;
        }
    }
    

    You can use meta_value instead of meta_value_num (available with v2.8), but I assume that price is a numeric value.

  2. If you find you lose your navigation menu, I updated @soju’s answer as follows:

    add_filter('pre_get_posts', 'pre_get_posts_hook' );
    
    function pre_get_posts_hook($wp_query) {
        if ( is_archive() && $wp_query->is_main_query() ) { //edited this line
            $wp_query->set( 'orderby', 'meta_value_num' );
            $wp_query->set( 'meta_key', 'price' );
            $wp_query->set( 'order', 'ASC' );
            return $wp_query;
        }
    }
    

    Then the filter won’t affect your nav menu

  3. I believe you will find this functionality under a class called PostsOrderedByMetaQuery that extends WP_Query and accepts new arguments ‘orderby_meta_key’ and ‘orderby_order’

    class PostsOrderedByMetaQuery extends WP_Query {
    var $posts_ordered_by_meta = true;
    var $orderby_order = 'ASC';
    var $orderby_meta_key;
    function __construct($args=array()) {
    
    add_filter('posts_join',array(&$this,'posts_join'),10,2);
    add_filter('posts_orderby',array(&$this,'posts_orderby'),10,2);
    $this->posts_ordered_by_meta = true;
    $this->orderby_meta_key = $args['orderby_meta_key'];
    unset($args['orderby_meta_key']);
    if (!empty($args['orderby_order'])) {
      $this->orderby_order = $args['orderby_order'];
      unset($args['orderby_order']);
    }
    parent::query($args);
    }
    function posts_join($join,$query) {
    if (isset($query->posts_ordered_by_meta)) {
      global $wpdb;
      $join .=<<<SQL
     INNER JOIN {$wpdb->postmeta} postmeta_price ON postmeta_price.post_id={$wpdb->posts}.ID
       AND postmeta_price.meta_key='{$this->orderby_meta_key}'
    SQL;
    }
    return $join;
    }
    function posts_orderby($orderby,$query) {
    if (isset($query->posts_ordered_by_meta)) {
      global $wpdb;
      $orderby = "postmeta_price.meta_value {$this->orderby_order}";
     }
    return $orderby;
     }
    }
    

    You would call it like this:

      $thirtydays = date('Y/m/d', strtotime('+30 days'));
      $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
      $query = new PostsOrderedByMetaQuery(array
      (
      'post_type' => array('post', 'real-estate'),
      'meta_key' => 'Time Available',
      'meta_compare' => '<=',
      'meta_value' => $thirtydays,
      'paged' => $paged,
      'orderby_meta_key' => 'Price',
      'orderby_order'    => 'DESC',
       ));
    
      foreach($query->posts as $post) 
      {
          echo " {$post->post_title}n";
      }
    

    You can copy the PostsOrderedByMetaQuery class to your theme’s functions.php file, or you can use it within a .php file of a plugin you may be writing.