Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I've read umpteen answers and questions about things that are very similar to this but I just can't get it to work for my specific situation.

I have some posts with a category of "property". In category.php I want these posts to appear in ascending date order initially (as per usual).

I then want to have a dropdown menu, which gives the option of viewing:

  • price high to low
  • price low to high
  • latest (default).

Each post has been assigned a custom field of "property_price" which is a numeric value. I also need pagination to work.

Initial goes with this as my query_posts:

<?php
if ( have_posts() ) :
  $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
  $args = array(
     'meta_key' => 'property_price',
     'orderby'  => 'meta_value_num',
     'order'    => 'ASC',
     'paged'    => $paged
  );
  query_posts( $args );
  while ( have_posts() ) :
    the_post();

This just renders a list obviously. However, even at this point, I am stuck as WordPress insists on ordering things like this: 1, 10, 100, 2, 3 so not actually in proper order. I thought using 'orderby' => 'meta_value_num' was supposed to sort this out?

If anyone can help I'd be very grateful. Be gentle on me, I'm no expert so fool-proof instructions gladly received.

Thanks in advance.

share|improve this question
3  
I strongly recommend against using query_posts –  Tom J Nowell May 13 at 14:05

3 Answers 3

i had this problem once, i believe you custom fiel is not set as INT right? for meta_value_num work it should be in INT, not in string, even that you write only numbers in it

share|improve this answer
2  
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. You can also add a bounty to draw more attention to this question. –  Pieter Goosen May 13 at 14:04
1  
I can imagine how this might be an attempt to offer a solution to the problem. However, as written it looks like a request for more information rather than an answer, and it is very minimal and not likely very useful to anyone not already an expert in the field (which is not most people asking questions here). Please provide more detail and explain the logic of the solution. See How to Answer. –  s_ha_dum May 13 at 14:05

You are doing a few things in a not so ideal way. So here is how to solve your problem.

1. Create a category-property.php template file

Paste a standard WordPress loop in this file. If you visit the Property category in your site, you should only see the posts filed in this category, sorted by date (newest to oldest).

By using the Template Hierarchy (http://codex.wordpress.org/Template_Hierarchy), we now have a page that displays the posts we want.

If you install the Debug Bar Plugin (http://wordpress.org/extend/plugins/debug-bar/) you can verify that category-property.php is used for this query.

2. Modify the main query via the pre_get_posts filter

If you use query_posts(), this runs an additional query to the main query, which is not only a waste of resources, but also results in plenty of problems with pagination not working etc..

So what we'll do is write a function (that needs to be in functions.php) that alters the main query on a specific page to sort by the order that we want.

function my_prefix_sort_property_category( $query ) {
    // if we're not in the admin, and it's the main query, and it's a property category
    if ( ! is_admin() && $query->is_main_query() && is_category( 'Property' ) ) {
        // then modify the main query
        $query->set( 'meta_key', 'property_price' );
        $query->set( 'orderby', 'meta_value_num' );
        $query->set( 'order', 'ASC' );
    }
}
add_action( 'pre_get_posts', 'my_prefix_sort_property_category' );
share|improve this answer

'meta_value_num' is not magic, it cast a value as numeric, however, to be properly casted as numeric, a value must be numeric compatible.

In OP is said that the field values are "numeric" but I bet there's something not numeric e.g. a currency symbol like "10 $" or "$ 10.00", or other symbol like "10-".

Also consider that using a thousand separator most probably makes the casting fail, because MySQL can mistake it as a decimal separator.

When you want to use a meta field for order, follow this rules:

  • Do not use any other char that is not numeric (no currency or other symbols, no spaces..)
  • Do not use any thousands separator
  • When a value is an integer (no decimal) do not use decimal, e.g. if a value is 10, do not use 10.00
  • Use the dot as decimal separator.

If you follow previous rules the order by 'meta_value_num' will work as expected.

If you are asking yourself how to properly format a price entered using previous rules, the answer is number_format.

As example, in Italy, a properly formatted price is € 1.000,50 (one thousand euros and fifty cents).

Following previous rules, one should enter that price as 1000.50

To output the proper formatted price is possible to use a custom function like this:

function formatted_price( $number ) {
  // force 2 decimals, comma as decimal separator and dot as thousand separator
  return '&euro; ' . number_format ( (float) $number, 2, ",", "." );
}

And use it like so:

while ( have_posts() ) :
  the_post();

  if ( $price = get_post_meta( get_the_ID(), 'property_price', TRUE ) ) {
     // assuming the price is stored in the 'property_price' custom field
    echo formatted_price( $price );
  }

That said, I strongly suggest you to avoid the usage of query_posts and replace it with a function hooked into 'pre_get_posts'.

From Codex:

... It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination). Any modern WP code should use more reliable methods, like making use of pre_get_posts hook, for this purpose.

So, remove any code from your category.php and in your functions.php write:

add_action( 'pre_get_posts', 'order_properties_by_price' );

function order_properties_by_price( $query ) {
  if ( is_admin() || ! $query->is_main_query() || ! $query->is_category('property') )
      return;
  $byprice = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_STRING ); 
  if ( empty( $byprice ) )
      return;
  if ( $byprice === 'price' ) {
    $order = strtoupper( filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING ) ); 
    if ( $order !== 'DESC' ) $order = 'ASC';
    $query->set( 'meta_key', 'property_price' );
    $query->set( 'orderby', 'meta_value_num' );
    $query->set( 'order', $order );
  }
}

Now, assuming that your 'property' archive url is

http://www.example.com/category/property/

it will, of course, show posts in category 'property' ordered by date (newer to older).

Using the code posted above, the url

http://www.example.com/category/property/?orderby=price

will show posts in category 'property' ascending ordered by price, and the url

http://www.example.com/category/property/?orderby=price&order=DESC

will show posts in category 'property' descending ordered by price.

So, what you need to do, is to make a dropdown that redirect page to proper urls.

You can write a custom function to output the dropdown:

function order_properties_menu() {
  if ( ! is_category('property') ) return;
  $url = get_category_link( get_term_by( 'slug', 'property', 'category' ) );
  $form = '<form id="orderbyprice" action="#" method="GET">'
      . '<label>Order by:</label><select name="order">';
  $htl = add_query_arg( array('orderby' => 'price', 'order' => 'DESC'), $url );
  $lth = add_query_arg( array('orderby' => 'price', 'order' => 'ASC'), $url );
  $options = array(
    'latest' => array( 'Latest', $url ),
    'htl'    => array( 'Price High to Low', $htl ),
    'lth'    => array( 'Price Low to High', $lth ) 
  );
  $format = '<option value="%s"%s>%s</option>';
  foreach( $options as $id => $option ) {
    $sel = 'latest';
    if ( get_query_var('orderby') === 'meta_value_num' )
        $sel = get_query_var('order') === 'DESC' ? 'htl' : 'lth';
    $selected = selected( $sel, $id, FALSE );
    $form .= sprintf( $format, esc_url( $option[1] ), $selected, esc_html( $option[0] ) );
  }
  echo $form . '</select></form>';
  ?>
  <script>
  jQuery(document).on( 'change', '#orderbyprice select', function() {
    window.location.href = jQuery(this).val();
  });
  </script>
  <?php
}

This function output a dropdown menu that allow users to choose from the 3 ordering options, and when one is selected the page is redirected accordingly.

After having added this function in functions.php, put everywhere you want in your category.php

order_properties_menu();

and the select will be shown.

Note that order_properties_menu() function requires jQuery.

share|improve this answer
    
Just a small side note about the number_format function. WordPress has its own wrapper for it that formats a number to the current locale number_format_i18n. Just to be completive. –  TV productions May 16 at 15:09

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.