2
votes

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.

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.

3

3 Answers

6
votes

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.

1
votes

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

0
votes

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.