1

On my search.php page, I have a "Sort By" dropdown that almost works exactly how I want it to --

<select class="dropdown-class" name="sort-posts" id="sortbox" onchange="document.location.href=location.href+this.options[this.selectedIndex].value;">
<option disabled>Sort by</option>
<option value="&orderby=date&order=dsc">Newest</option>
<option value="&orderby=date&order=asc">Oldest</option>
</select>

<script type="text/javascript">

    <?php if (( $_GET['orderby'] == 'date') && ( $_GET['order'] == 'dsc')) { ?>
        document.getElementById('sortbox').value='orderby=date&order=dsc';

    <?php } elseif (( $_GET['orderby'] == 'date') && ( $_GET['order'] == 'asc')) { ?>
        document.getElementById('sortbox').value='orderby=date&order=asc';

    <?php } else { ?>
        document.getElementById('sortbox').value='orderby=date&order=desc';

    <?php } ?>

</script>

When Im on the search results page, the sort dropdown will sort the current results according to date by grabbing the url and appending to it then reloading the page with the results ---

// Before
mydomain.com/?s=Search&property_city=new-york-city&beds=Any 

// After
mydomain.com/?s=Search&property_city=new-york-city&beds=Any&orderby=date&order=dsc

However, I am now trying to improve the code further by using it to sort based on numeric custom fields (high to low and low to high).

It seems all the info I can find on the subject have much more complicated ways of doing so. Is there anyway of doing this using the code I already started on?

UPDATE

I seem to be getting closer -

On my search.php page I have this before my loop ----

<select class="dropdown-class" name="sort-posts" id="sortbox" onchange="document.location.href=location.href+this.options[this.selectedIndex].value;">
<option disabled>Sort by</option>
<option value="&orderby=date&order=dsc">Newest</option>
<option value="&orderby=date&order=asc">Oldest</option>
<option value="&orderby=property_price&order=asc">Most Expensive</option>
<option value="&orderby=property_price&order=dsc">Least Expensive</option>
<option value="&orderby=property_area&order=dsc">Largest</option>
<option value="&orderby=property_area&order=asc">Smallest</option>
</select>

<script type="text/javascript">

    <?php if (( $_GET['orderby'] == 'date') && ( $_GET['order'] == 'dsc')) { ?>
        document.getElementById('sortbox').value='orderby=date&order=dsc';

    <?php } elseif (( $_GET['orderby'] == 'date') && ( $_GET['order'] == 'asc')) { ?>
        document.getElementById('sortbox').value='orderby=date&order=asc';

    <?php } elseif (( $_GET['orderby'] == 'property_price') && ( $_GET['order'] == 'asc')) { ?>
        document.getElementById('sortbox').value='orderby=property_price&order=asc';

        <?php } elseif (( $_GET['orderby'] == 'property_price') && ( $_GET['order'] == 'dsc')) { ?>
        document.getElementById('sortbox').value='orderby=property_price&order=dsc';

    <?php } elseif (( $_GET['orderby'] == 'property_area') && ( $_GET['order'] == 'asc')) { ?>
        document.getElementById('sortbox').value='orderby=property_area&order=asc';

        <?php } elseif (( $_GET['orderby'] == 'property_area') && ( $_GET['order'] == 'dsc')) { ?>
        document.getElementById('sortbox').value='orderby=property_area&order=dsc';


    <?php } else { ?>
        document.getElementById('sortbox').value='orderby=date&order=desc';

    <?php } ?>

</script>

where it says "orderby=xxx" I just use my names for my custom fields.

1
  • No I thought it was working but it's actually just sorting them randomly. – Rich Dec 28 '17 at 21:22
1

orderby should be e.g. meta_value or meta_value_num – various specific meta_type's are available – to order by meta data. Additionally a meta_key has to be set and present in the query. To be exact and to avoid misunderstandings, meta_key has to be the keyname of the field you're sorting by. Take a closer look at the Order & Orderby Parameters and Custum Fields Parameters section of the WP_Query documentation for more information.

4
  • Like this? <option value="&orderby=meta_value_num&meta_type=numeric&meta_key=property_price&order=asc">Most Expensive</option> – Rich Dec 29 '17 at 0:31
  • @RichJ No, meta_type is an aspect of meta_value_*, not a parameter. So while you could use meta_value_numeric, there already exists the predating meta_value_num. There are other types, like CHAR, DATE, TIME and so forth. All this is part of the documentaion, I would recommend to read it. – Nicolai Dec 29 '17 at 1:03
  • I have, and I've tried applying it to my situation but with no luck. – Rich Dec 29 '17 at 1:36
  • @RichJ Setting orderby to meta_value_num and meta_key to a numeric field should do the trick. – Nicolai Dec 29 '17 at 1:53
1

Ok I got it figured out using @Nicolai suggestion and also a answer from another question (which I seem to have lost the link to).

For starters, I had to make sure my numbers have no commas in it which I had already done by saving my post meta without it.

Then the code I found in another question, I used and placed into my functions.php file --

function wpse139657_orderby(){
    if( isset($_GET['orderby']) ){
        $order = $_GET['order'] or 'DESC';
        set_query_var('orderby', 'meta_value_num');
        //set_query_var('meta_type', 'numeric');
        set_query_var('meta_key', $_GET['orderby']);
        set_query_var('order', $order);
    }
}

add_filter('pre_get_posts','wpse139657_orderby');

Then on my search.php page I used the following code for the select dropdown --

<select class="dropdown-class" name="sort-posts" id="sortbox" onchange="document.location.href=location.href+this.options[this.selectedIndex].value;">
<option disabled>Sort by</option>
<option value="&orderby=date&order=dsc">Newest</option>
<option value="&orderby=date&order=asc">Oldest</option>
<option value="&orderby=property_price2&order=DESC">Most Expensive</option>
<option value="&orderby=property_price2&order=ASC">Least Expensive</option>
<option value="&orderby=area2&order=DESC">Largest</option>
<option value="&orderby=area2&order=ASC">Smallest</option>
</select>

This seems to do the trick and works with pagination, only issues I have right now is --

  1. Ugly Urls
  2. When the page is reloaded with the posts sorted, the dropdown select goes back to default ie "Newest" when it should still be on the option the user chose.
3
  • 1
    You are looking for this one: wordpress.stackexchange.com/q/139657 – Nicolai Dec 29 '17 at 3:18
  • It should be set_query_var( 'orderby', $_GET[ 'orderby' ] ); and set_query_var( 'meta_key', $_GET[ 'meta_key' ] ), if you decide to set it up properly. – Nicolai Dec 29 '17 at 3:27
  • Problem 2 is because you are staring all values with "&". remove it. – Michael Rogers Mar 23 '18 at 13:18
1

Form will automaticly add your select value to url when you submit form. There is no need to create such a value &orderby=date&order=dsc. If you want to pass multiple information in single select value you can use simpler value and if statement. For newest option I would use newest as value and add make such a if statement.

// Url /?s=test&order=newest

$orderby = '';
$order = '';

// Get filtered $_GET['order'] parameter
$value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );

if( $value === 'newest' ) {
    $orderby = 'date';
    $order = 'desc';
}

// Modify query using $orderby and $order parameters

Using JavaScript to select option for me is also bad idea. You should check current order parameter and add selected attribute to your option.

// Url /?s=test&order=newest

// Get filtered $_GET['order'] parameter
$value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
$selected = ($value === 'newest') ? 'selected': '' ;
?>

<select name="order">
    <option value="newest" <?php echo esc_attr( $selected ); ?>></option>
</select>

I think you should not bother making nice url. For a small number of parameters it might be tempting to make nice url but when your app is getting bigger adding additional rewrite case and logic to it is purposeless. If you have to convince your boss or client to give up on nice urls show them Amazon on search page. I'm considering them as specialist and they do not use nice urls during search.

Code below is fully working example how to sort post by date, price and size. Execute wpse_288655_display_form function in place where you want to display the sorting form.

/**
 * Display sort form
 */
function wpse_288655_display_form() {

    /**
     * Get all params from url which are not part of our sort form
     * and display it in form as hidden inputs.
     */
    $search_param = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );

    /**
     * Current order value to select proper field
     */
    $value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
    ?>

    <form method="get">
        <?php wpse_288655_display_select('order', 'Sort by:', wpse_288655_get_order_by_options(), $value ); ?>

        <?php
            /**
             * Display all params from url which do not apply to our sort form
             */
        ?>
        <input type="hidden" name="s" value="<?php esc_attr_e( $search_param ); ?>">

        <button type="submit"><?php esc_html_e('Sort'); ?></button>
    </form>
    <?php
}

/**
 * Get sort options
 */
function wpse_288655_get_order_by_options(){

    return array(
        '' => '',
        'newest' => __('Newest'),
        'oldest' => __('Oldest'),
        'most_expensive' => __('Most Expensive'),
        'least_expensive' => __('Least Expensive'),
        'largest' => __('Largest'),
        'smallest' => __('Smallest'),
    );
}

/**
 * Display select field
 */
function wpse_288655_display_select( $name, $label, $options, $value = '' ) {
    ?>
    <label><?php esc_html_e( $label ) ?></label>
    <select name="<?php esc_attr_e( $name ) ?>">
        <?php wpse_288655_display_options( $options, $value ); ?>
    </select>
    <?php
}

/**
 * Display select options
 */
function wpse_288655_display_options( $options, $value ) {

    foreach( $options as $option_value => $option_label ):
        $selected = ( $option_value === $value ) ? ' selected' : '';
        ?>
        <option value="<?php esc_attr_e( $option_value ) ?>"<?php esc_attr_e( $selected ) ?>><?php esc_html_e( $option_label ) ?></option>
        <?php

    endforeach;
}

/**
 * Sort posts using pre_get_posts filter
 */
function wpse_288655_order_posts( $query ) {

    /**
     * Execute query only when we are on search page and this is main query
     */
    if ( $query->is_search() && $query->is_main_query() ) {

        $value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
        $order_by_options = wpse_288655_get_order_by_options();

        if( isset( $order_by_options[ $value ] ) && !empty( $value ) ) {

            switch( $value ) {
                case 'newest':

                    $query->set( 'order', 'desc' );
                    $query->set( 'orderby', 'date' );

                    break;

                case 'oldest':

                    $query->set( 'order', 'asc' );
                    $query->set( 'orderby', 'date' );

                    break;
                case 'most_expensive':

                    $query->set( 'meta_key', 'price' ); // Your custom meta_key

                    $query->set( 'order', 'desc' );
                    $query->set( 'orderby', 'meta_value_num' );

                    break;
                case 'least_expensive':

                    $query->set( 'meta_key', 'price' ); // Your custom meta_key

                    $query->set( 'order', 'asc' );
                    $query->set( 'orderby', 'meta_value_num' );

                    break;
                case 'largest':

                    $query->set( 'meta_key', 'size' ); // Your custom meta_key

                    $query->set( 'order', 'desc' );
                    $query->set( 'orderby', 'meta_value_num' );

                    break;
                case 'smallest':

                    $query->set( 'meta_key', 'size' ); // Your custom meta_key

                    $query->set( 'order', 'asc' );
                    $query->set( 'orderby', 'meta_value_num' );

                    break;
                default:
                    break;
            }
        }
    }
}

add_filter('pre_get_posts', 'wpse_288655_order_posts');
2
  • it has to be dynamic though, without a submit button – Rich Dec 30 '17 at 8:59
  • 1
    Just trigger submit event on form when select change using JavaScript. – kierzniak Dec 30 '17 at 9:12
-1

For the below answers look very uncertain - for example you can not just hook pre_get_posts because it affects every query on the website, even in WordPress admin area.

My first recommendation for you is to refuse of using JavaScript, I'd like to make your dropdown to look like this:

<form action="" method="GET"><select name="sortposts">
    <option disabled>Sort by</option>
    <option value="date-DESC">Newest</option>
    <option value="date-ASC">Oldest</option>
    <option value="property_price-ASC">Most Expensive</option>
    <option value="property_price-DESC">Least Expensive</option>
.....

Great, now in the same search.php file you can do this:

<?php
    global $wp_query;

    // let's create new array with args
    if( !empty( $_GET['sortposts'] ) ) {
        // I recommend to explode it because we're take 2 param values from one $_GET variable
        $sort_args = explode( "-", $_GET['sortposts'] );
        // $sort_args[0] is what we sort
        // $sort_args[1] is how we sort - Ascending or Descending
        $new_args['order'] = $sort_args[1];
        if( $sort_args[0] == 'date' ) {
            // date is a default WP_Query value, so we do nothing
            $new_args['orderby'] == 'date';
        } elseif( $sort_args[0] == 'property_price' ) {
            $new_args['orderby'] = 'meta_value_num';
            $new_args['meta_key'] = 'THIS IS A PROPERTY PRICE META KEY';
        }
    }

    $args = array_merge( $wp_query->query_vars, $new_args );
    query_posts( $args );

Something like that. But you can use pre_get_posts of course if you want. Just add conditions there, so the filter won't fire on non-relevant pages.

If you would like to implement the filtering with AJAX, you can look at this tutorial as well https://rudrastyh.com/wordpress/ajax-load-more-with-filters.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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