1

This is not really much of a technical question but it is more like an intro to the topic I want to talk about.

So this is my first time creating an advanced search custom page in which I need to get post according to the custom post type, custom taxonomies, years and the order by.

The problem is that I really have not idea in how to star here. I've been looking for tutorials or snippets in the Internet but everything I find somehow can not work with dropdowns(why?, i don't know), all the tutorials that I have found are about using check-boxes.

So any recommendation or function code you guys can tell me about?.

Thanks in advance.

This is the HTML code I want to use:

<!-- Post Type -->
<div class="col-md-3">
    <div class="form-group">
        <label for="post_type">Post Type</label>
        <select class="form-control">
            <option>Any</option>
            <option>Post</option>
            <option>Portfolio</option>
            <option>Snippet</option>
        </select>
    </div>
</div>

<!-- Taxonomy -->
<div class="col-md-3">
    <div class="form-group">
        <label for="post_type">Taxonomy</label>
        <select class="form-control" multiple>
            <option>Any</option>
            <option>category</option>
            <option>portfolio_categories</option>
            <option>snippets_categories</option>
        </select>
    </div>
</div>

<!-- Year -->
<div class="col-md-3">
    <div class="form-group">
        <label for="post_type">Year</label>
        <select class="form-control">
            <option>Any</option>
            <option>2018</option>
            <option>2017</option>
            <option>2016</option>
            <option>2015</option>
            <option>2014</option>
        </select>
    </div>
</div>

<!-- Orderby -->
<div class="col-md-3">
    <div class="form-group">
        <label for="post_type">Order by</label>
        <select class="form-control" multiple>
            <option>Any</option>
            <option>Author</option>
            <option>Popularity (# of Comments)</option>
            <option>Views post_views_count</option>
            <option>Year</option>
            <option>ASC</option>
            <option>DESC</option>
        </select>
    </div>
</div>

<!-- Search Button -->
<div class="col-md-12">
    <input type="submit" class="btn btn-primary" id="buscar_btn" value="Search">
    <noscript>&lt;b&gt;Your browser does not support Javascript, this making it unable to display the posts.&lt;/b&gt;</noscript>
    <div id="resultados"><div class="cargando_medio"></div></div>
</div>

Here is the function for the post_view_count custom field:

// Function to display number of views.
function getPostViews($postID){
    $count_key = 'post_views_count';
    $count = get_post_meta($postID, $count_key, true);
    if($count==''){
        delete_post_meta($postID, $count_key);
        add_post_meta($postID, $count_key, '0');
        return "0 Views";
    }
    return $count.' Views';
}

// Function to count views.
function setPostViews($postID) {
    $count_key = 'post_views_count';
    $count = get_post_meta($postID, $count_key, true);
    if($count==''){
        $count = 0;
        delete_post_meta($postID, $count_key);
        add_post_meta($postID, $count_key, '0');
    }else{
        $count++;
        update_post_meta($postID, $count_key, $count);
    }
}


// Add it to a column in WP-Admin
function posts_column_views($defaults){
    $defaults['post_views'] = __('Views');
    return $defaults;
}
add_filter('manage_posts_columns', 'posts_column_views');
function posts_custom_column_views($column_name, $id){
    if($column_name === 'post_views'){
        echo getPostViews(get_the_ID());
    }
}
add_action('manage_posts_custom_column', 'posts_custom_column_views',5,2);

The function is not really good, actually is not good but it does the work. I may have to change it later on, especially because I want it to count views per IP address and per day; it currently does it by counting the views per every REFRESH I do in the current page/post.

enter image description here

7
  • Are you looking for a function that will search posts based on the selected options? Can you post the code for the advanced search form?
    – Sally CJ
    Commented Apr 17, 2018 at 6:43
  • Yes ,see my updated question. I'm trying to use Ajax so there's not really a form. I just need the function and from there I can create the Ajax calls that I need to make it work.
    – Kirasiris
    Commented Apr 17, 2018 at 6:52
  • I see. But are the Taxonomy options just placeholders? I mean, you don't have a taxonomy named "1", "2", etc., do you? And same to the other select menus; I suppose you'd use the correct value later on?
    – Sally CJ
    Commented Apr 17, 2018 at 6:59
  • Yes, do you need the years and the order by info as well? ... I mean there's not really anything I have customized in there. If I'm not mistaken theres a function in WordPress that I can use to foreach the years and the orderby select.. I will look into that after I'm done with the post type ans taxonomies.
    – Kirasiris
    Commented Apr 17, 2018 at 7:07
  • I actually have the function for querying the posts based on the options you defined (e.g. post type and taxonomy); however, it doesn't include the part which sorts the results by "Views". Do your posts have a custom field for the "View" part?
    – Sally CJ
    Commented Apr 18, 2018 at 3:42

1 Answer 1

3

Try this:

PHP

add_action( 'wp_ajax_my_adv_search', 'ajax_my_adv_search' );
add_action( 'wp_ajax_nopriv_my_adv_search', 'ajax_my_adv_search' );
function ajax_my_adv_search() {
    if ( ! check_ajax_referer( 'my-adv-search', 'q_nonce', false ) ) {
        echo 'session_expired';
        wp_die();
    }

    $post_type = isset( $_POST['q_post_type'] ) ? $_POST['q_post_type'] : '';
    $taxonomy = isset( $_POST['q_taxonomy'] ) ? $_POST['q_taxonomy'] : [];
    $year = isset( $_POST['q_year'] ) ? $_POST['q_year'] : '';
    $orderby = isset( $_POST['q_orderby'] ) ? $_POST['q_orderby'] : [];
    $order = isset( $_POST['q_order'] ) ? $_POST['q_order'] : '';

    // Note that if $post_type is 'any', all post statuses will be included. In
    // that case, you may want to set specific post statuses below.
    $post_status = '';

    $taxonomy = array_filter( (array) $taxonomy );
    if ( ! in_array( 'any', $taxonomy ) ) {
        $taxonomy = array_unique( array_map( 'trim', $taxonomy ) );

        add_filter( 'posts_join', function( $c ) use ( $taxonomy ) {
            if ( ! empty( $taxonomy ) ) {
                global $wpdb;
                // 1 below is one/number and not the lowercase of L
                $c .= " INNER JOIN {$wpdb->term_relationships} AS ctr1 ON ctr1.object_id = {$wpdb->posts}.ID" .
                    " INNER JOIN {$wpdb->term_taxonomy} AS ctt1 ON ctt1.term_taxonomy_id = ctr1.term_taxonomy_id";
            }
            return $c;
        } );

        add_filter( 'posts_where', function( $c ) use ( $taxonomy ) {
            if ( ! empty( $taxonomy ) ) {
                $tax_list = array_map( 'esc_sql', $taxonomy );
                $tax_list = "'" . implode( "', '", $tax_list ) . "'";

                // 1 below is one/number and not the lowercase of L
                $c .= " AND ( ctt1.taxonomy IN ($tax_list) )";
            }
            return $c;
        } );
    }

    if ( ! is_numeric( $year ) ) {
        $year = '';
    }

    $orderby = array_filter( (array) $orderby );
    if ( in_array( 'any', $orderby ) ) {
        // Don't sort by post date.
        $orderby2 = false;
    } else {
        $orderby = array_unique( array_map( 'trim', $orderby ) );

        // TRUE if we're sorting by year.
        $ob_year = false;

        foreach ( $orderby as $i => $s ) {
            // Sort posts by year.
            if ( 'year' === $s ) {
                $ob_year = true;
                unset( $orderby[ $i ] );
            }

            // Sort posts by views count. Note that this would only return
            // posts that have the custom field 'post_views_count'.
            if ( 'views_count' === $s ) {
                $meta_key = 'post_views_count';
                $orderby2 = 'meta_value_num';
                unset( $orderby[ $i ] );
            }
        }

        add_filter( 'posts_orderby', function( $c, $q ) use ( $ob_year ) {
            if ( $ob_year ) {
                global $wpdb;

                // Use the value parsed by WP_Query.
                $order = $q->get( 'order' );

                $c .= $c ? ', ' : '';
                $c .= "YEAR({$wpdb->posts}.post_date) $order";
            }
            return $c;
        }, 10, 2 );

        $ok = isset( $orderby2 );
        if ( ! $ok && empty( $orderby ) ) {
            // Don't sort by post date.
            $orderby2 = false;
        } elseif ( ! $ok ) {
            // Pass to WP_Query as a string.
            $orderby2 = implode( ' ', $orderby );
        }
    }

    $q = new WP_Query( [
        'post_status' => $post_status,
        'post_type'   => $post_type,
        'year'        => $year,
        'meta_key'    => isset( $meta_key ) ? $meta_key : '',
        'orderby'     => $orderby2,
        'order'       => $order,
    ] );

    if ( $q->have_posts() ) {
        echo '<ul>';
        while ( $q->have_posts() ) {
            $q->the_post();

            echo '<li>';
                the_title(); echo '; ID: '; the_ID();
            echo '</li>';
        }
        echo '</ul>';
    } else {
        echo '<p>No posts found.</p>';
    }

    wp_die();
}

HTML

Take a look at the "form" below, and make sure to set the proper ID in the corresponding select element/menu. You also need to add the 'order' fields in the "Order by" column, and nonce field before the submit/search button.

<div id="my-adv-search">
<!-- Post Type -->
<div class="col-md-3">
    <div class="form-group">
        <label for="q_post_type">Post Type</label>
        <select class="form-control" id="q_post_type">
            <option value="any" selected>Any</option>
            ...
        </select>
    </div>
</div>

<!-- Taxonomy -->
<div class="col-md-3">
    <div class="form-group">
        <label for="q_taxonomy">Taxonomy</label>
        <select class="form-control" multiple id="q_taxonomy">
            <option value="any" selected>Any</option>
            ...
        </select>
    </div>
</div>

<!-- Year -->
<div class="col-md-3">
    <div class="form-group">
        <label for="q_year">Year</label>
        <select class="form-control" id="q_year">
            <option value="any" selected>Any</option>
            ...
        </select>
    </div>
</div>

<!-- Orderby -->
<div class="col-md-3">
    <div class="form-group">
        <label for="q_orderby">Order by</label>
        <select class="form-control" multiple id="q_orderby">
            <option value="any" selected>Any</option>
            <?php
            foreach ( [
              'author'        => 'Author',
              'comment_count' => 'Popularity (# of Comments)',
              'year'          => 'Year',
              'views_count'   => 'Views',
            ] as $value => $label ) {
              printf( '<option value="%s">%s</option>',
                esc_attr( $value ), esc_html( $label ) );
            }
            ?>
        </select>
    </div>
    <label class="radio-inline">
      <input type="radio" name="order" id="q_order-asc" value="ASC" />
      ASC
    </label>
    <label class="radio-inline">
      <input type="radio" name="order" id="q_order-desc" value="DESC" checked />
      DESC
    </label>
</div>

<!-- Nonce field. -->
<?php wp_nonce_field( 'my-adv-search', 'q_nonce' ); ?>

<!-- Search Button -->
<div class="col-md-12">
    <input type="submit" class="btn btn-primary" id="buscar_btn" value="Search">
    <noscript>&lt;b&gt;Your browser does not support Javascript, this making it unable to display the posts.&lt;/b&gt;</noscript>
    <div id="resultados"><div class="cargando_medio"></div></div>
</div>
</div><!-- End #my-adv-search -->

JS/jQuery/AJAX

See below for a sample JS/jQuery script which does the AJAX search.

jQuery( function( $ ){
    var ajaxurl = '/path/to/wp-admin/admin-ajax.php';

    function searchPosts( btn ) {
        var _btn_text = btn.value,
            q_order;

        btn.disabled = true;
        btn.value = 'Searching..';

        q_order = $( '#q_order-asc' ).is( ':checked' ) ?
            'ASC' : 'DESC';

        return $.post( ajaxurl, {
            action: 'my_adv_search',
            q_nonce: $( '#q_nonce' ).val(),
            q_post_type: $( '#q_post_type' ).val(),
            q_taxonomy: $( '#q_taxonomy' ).val(),
            q_year: $( '#q_year' ).val(),
            q_orderby: $( '#q_orderby' ).val(),
            q_order: q_order,
        } ).done( function( s ){
            if ( 'session_expired' === s ) {
                location.reload();
                return;
            }

            $( '#resultados' ).html( s );
        } ).always( function(){
            btn.value = _btn_text;
            btn.disabled = false;
        } );
    }

    $( '#buscar_btn', '#my-adv-search' ).on( 'click', function( e ){
        e.preventDefault();

        // Run AJAX search.
        searchPosts( this );

        // Remove button focus.
        this.blur();
    } );
} );

Notes

  • In the ajax_my_adv_search() PHP function, I didn't use tax_query with the WP_Query call because we are querying for posts that are in one of the specified taxonomy keys (e.g. category for standard Post category, and post_tags for standard Post tags), and not in a term inside a taxonomy. Therefore, I used the posts_join and posts_where hooks in WP_Query to get the proper results for such kind of query.

  • In the ajax_my_adv_search() PHP function, I also used the posts_orderby hook in WP_Query because sorting posts/results by year is by default not available in the WP_Query class.

6
  • 1
    I'm sure it would help readers, if you explained why filtering joins and order of WP_Query is needed here, instead of e.g. using tax_query and ordering array parameters. ps: avoid possible SQL injections and include the relevant code, just in case that link changes or it's content is removed.
    – birgire
    Commented Apr 18, 2018 at 8:45
  • Thanks a lot @birgire. I completely forgot to escape the $taxonomy values.
    – Sally CJ
    Commented Apr 18, 2018 at 9:45
  • Thanks for the update. Sort by post date, will that not also implicilty give a sort by year?
    – birgire
    Commented Apr 18, 2018 at 13:05
  • 1
    What I had in mind was that the post date is already of the form yyyy-mm-dd HH:ii:ss and gives us the "natural" year order, by ordering by the date column. Another question arises is how will the order be for ORDER BY YEAR( wp_posts.post_date ), when all the posts have the same year. Just thinking of way to simplify and avoid extra manual SQL filter if possible ;-)
    – birgire
    Commented Apr 18, 2018 at 13:19
  • 1
    See this image. But anyway, I'll let the OP decides whether he wants (or meant) to sort the posts by the full post date or just the year. If he chooses (or actually wanted) the first option, then I'll edit the code.
    – Sally CJ
    Commented Apr 18, 2018 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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