0

I have a site that lists properties for sale. I have a custom wp_query, with a sort dropdown I created. This dropdown reorders the posts query by date, price (low-high & high-low) and beds (low-high & high-low). Price and beds are custom fields, with numeric values only. (no currency/comma's).

On change, AJAX reorganises the posts below the dropdown. This all works really well.

However, some properties do not have a price (instead, they show "price on request"). The problem is, if I put their price as "0", or leave the price field empty on these properties, then they appear in the sort order with a price of 0. So when I sort the properties by price low to high - they appear first in the listing.

I somehow need to run the sort normally, but if the property price == 0, then move them to the END of the sort order, regardless of the sort selected.

Is this possible? Maybe with pre_get_posts?

Here's my code:

<select name="sort_by" class="sort_by_dropdown">
    <option value="dateposted-desc">Sort by Newest</option>
    <option value="price-desc">Sort by Price (High to Low)</option>
    <option value="price-asc">Sort by Price (Low to High)</option>
    <option value="bedrooms-desc">Sort by Beds (Most to Least)</option>
    <option value="bedrooms-asc">Sort by Beds (Least to Most)</option>
  </select>

<ul id="main_posts" class="item-listings">
<?php 
// Build the inital Loop
$args = array(
    'posts_per_page' => 9,
    'paged' => $paged
);

query_posts( $args );

if( have_posts() ) :

    while( have_posts() ): the_post();

        get_template_part( 'template-parts/post/content', get_post_format() );

    endwhile;
endif;?>
</ul>

and the AJAX:

 // AJAX Stuff for filters

/*
 * Filter
 */
$('#filter').change(function(){

    $.ajax({
        url : prop_loadmore_params.ajaxurl,
        data : $('#filter').serialize(), 
        dataType : 'json',
        type : 'POST',
        success : function( data ){

            // reset current page to 1 when filters on
            prop_loadmore_params.current_page = 1;

            prop_loadmore_params.posts = data.posts;

            // set max page
            prop_loadmore_params.max_page = data.max_page;

            $('#main_posts').html(data.content);

            $(".price-txt").digits(); // Add the commas!

            // If not enough posts for 2nd page, hide loadmore
            if ( data.max_page < 2 ) {
                $('#prop_loadmore').hide();
            } else {
                $('#prop_loadmore').show();
            }
        }
    });

    return false;

});

...

add_action('wp_ajax_prop_filters', 'property_filters'); 
add_action('wp_ajax_nopriv_prop_filters', 'property_filters');

function property_filters(){

    //*
    // Sort by Args
    //*

        if( isset( $_POST['sort_by'] ) && $_POST['sort_by'] == 'price-desc' ) {
            $orderby = 'meta_value_num'; 
            $order = 'DESC';
            $meta_key = 'price';
        }

        elseif( isset( $_POST['sort_by'] ) && $_POST['sort_by'] == 'price-asc' ) {
            $orderby = 'meta_value_num'; 
            $order = 'ASC';
            $meta_key = 'price';
        }
        elseif( isset( $_POST['sort_by'] ) && $_POST['sort_by'] == 'bedrooms-desc' ) {
            $orderby = 'meta_value_num'; 
            $order = 'DESC';
            $meta_key = 'bedrooms';
        }
        elseif( isset( $_POST['sort_by'] ) && $_POST['sort_by'] == 'bedrooms-asc' ) {
            $orderby = 'meta_value_num'; 
            $order = 'ASC';
            $meta_key = 'bedrooms';
        }
        else {
            $orderby = 'date'; 
            $order = 'DESC';
            $meta_key = '';
        }

 if (!is_user_logged_in()) {
    $args = array(
        'posts_per_page' => 9, 
        'post_status' => 'publish',
        'paged' => $_POST['page'],
        'meta_key' => $meta_key,
        'orderby' => $orderby,
        'order' => $order


    );
}
else {
    $args = array(
        'posts_per_page' => 9, 
        'meta_key' => $meta_key,
        'orderby' => $orderby,
        'order' => $order,
        'post_status' => array('publish', 'private'),
        'paged' => $_POST['page']
    );
}

    query_posts( $args );

    global $wp_query;

    if( have_posts() ) :

        ob_start();

        while( have_posts() ): the_post();

            get_template_part( 'template-parts/post/content', get_post_format() );

        endwhile;

        $posts_html = ob_get_contents(); 
        ob_end_clean(); 
    else:
        $posts_html = '<p>Nothing found for your criteria.</p>';
    endif;

    echo json_encode( array(
        'posts' => json_encode( $wp_query->query_vars ),
        'max_page' => $wp_query->max_num_pages,
        'found_posts' => $wp_query->found_posts,
        'content' => $posts_html
    ) );

    die();
}

(Side note: query posts is used because of ajax pagination combined with this, but I have removed that part of the code as I'm trying to keep the request simple!) :)

1 Answer 1

0

Yeah, so: First: you should not use query_posts. No, not even if you need ajax pagination. However, this is not your current problem.

To be able to sort the "price on request" Properties to the end of the list regardless of the price-sorting, you simply need to save the "price on request" information in another meta field. The reason you need to do this is the following: pagination. Let's say you got 20 properties, and 10 of them are "price on request" or "0" in your case. The moment you sort price by lowest to highest, your database request will return ONLY price on request properties, even though there are some with prices.

So what you need to do is sort them right within the database query.

Step 1: Save the "price on request" information in another meta field. Let's call it "priceonrequest". I would recommend using a checkbox in the backend, but i don't know how you build your meta inputs, and checkbox meta fields can be tricky, so let's say you use a text input. You enter 0s for "normal" properties and 1s for "price on request" properties.

Step 2: Throw out the meta_key attributes. meta_query is the stuff you want to use. Change the upper part of your function to this:

function property_filters(){
        //*
        // Sort by Args
        //*
        $args = array(
            'posts_per_page' => 9, 
            'post_status' => is_user_logged_in() ? array('publish', 'private') : array('publish'),
            'paged' => $_POST['page']
        );
    if(isset($_POST['sorty_by'])){
        $sortby = $_POST['sort_by'];
        switch($sortby){
            case "price-desc":
                $ascdesc = 'DESC';
                $meta_query = array(
                        'price_on_request' => array(
                            'key' => 'priceonrequest',
                            'compare' => 'EXISTS',
                        ),
                        'price' => array(
                            'key' => 'price',
                            'compare' => 'EXISTS',
                        ),
                    );
                    $args['meta_query'] = $meta_query;
                    $args['orderby'] = array(
                        'price_on_request' => 'ASC',
                        'price' => $ascdesc
                    );
            case "price-asc":
                $ascdesc = 'ASC';
                $meta_query = array(
                    'price_on_request' => array(
                        'key' => 'priceonrequest',
                        'compare' => 'EXISTS',
                        'type' => 'numeric',
                    ),
                    'price' => array(
                        'key' => 'price',
                        'compare' => 'EXISTS',
                        'type' => 'numeric',
                    ),
                );
                $args['meta_query'] = $meta_query;
                $args['orderby'] = array(
                    'price_on_request' => 'ASC',
                    'price' => $ascdesc
                );
            break;
            case "bedrooms-desc":
                $ascdesc = 'DESC';
                $meta_query = array(
                    'bedrooms' => array(
                        'key' => 'bedrooms',
                        'compare' => 'EXISTS',
                        'type' => 'numeric',
                    ),
                );
                $args['meta_query'] = $meta_query;
                $args['orderby'] = array(
                    'bedrooms' => $ascdesc
                );
            break;
            case "bedrooms-asc":
                $ascdesc = 'ASC';
                $meta_query = array(
                    'bedrooms' => array(
                        'key' => 'bedrooms',
                        'compare' => 'EXISTS',
                        'type' => 'numeric',
                    ),
                );
                $args['meta_query'] = $meta_query;
                $args['orderby'] = array(
                    'bedrooms' => $ascdesc
                );

            break;
            default:   
            break;
        }
    }
    query_posts( $args );
    //...

Your results will be sorted by the "priceonrequest" field ASC first (so the "Price on Request" properties will be the last ones no matter what) and THEN by the price field (which will now be nicely sorted).

You can read more about the Meta Query here: https://developer.wordpress.org/reference/classes/wp_meta_query/

and about the ordering here: https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/

Disclaimer: I did not test this code. It should work from Wordpress 4.2 on, maybe i got a typo in there somewhere.

Happy Coding!

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