2

I'm trying to build a search page for music tracks that lets visitors:

  • Choose the post type
  • Choose a genre
  • Choose a mood
  • Specify a Beats Per Minute (bpm) range

wp_dropdown_categories() is working for the genres and moods it seems (i.e. I don't need to modify pre_get_posts), but I can't limit posts to only those within a specific 'bpm' range.

The code I've written below either returns no results at all if I select certain ranges like 20 for 'bpm_start' and 180 for 'bpm_finish' or I get strange results for searches like this:

http://www.domain.com/?s=&post_type=ibm_tracks&ibm_genres=ambient-music&ibm_moods=0&bpm_start=20&bpm_finish=180

That should return a bunch of ibm_tracks posts from the ambient-music genre and within a bpm custom field that is between 10 and 180, but it's returning no posts for some reason...as a side note, no mood was specified in the example above, but choosing a mood doesn't affect the result anyway, no posts are returned either way.

Can anyone see what I'm doing wrong?

Thanks,

Osu

Form

<?php 
function osu_list_terms_dd($taxonomy_type, $taxonomy, $showalltext) {
    $args = array(
        'taxonomy'          => $taxonomy_type,
        'hide_empty'        => true,
        'name'              => $taxonomy_type,
        'value_field'       => 'slug',
        'show_option_all'   => $showalltext,
        'echo'              => 0,
        //'show_count'      => 1, // Useful for checking how many tracks are in a taxonomy term
        'id'                => 'taxonomy-filter__select-' . $taxonomy,
        'orderby'           => 'name',
        'order'             => 'ASC'
    );
    return wp_dropdown_categories($args);
}
?>

<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">

    <p>Search keywords:</p>
    <input type="search" value="<?php echo get_search_query(); ?>" name="s" />

    <p>Choose post type</p>
    <select name="post_type">
        <option value="ibm_tracks">TRACKS</option>
        <option value="ibm_logos">LOGOS</option>
    </select>

    <p>Choose genre</p>
    <?php echo osu_list_terms_dd('genres', 'ibm_genres', '- choose a genre -'); ?>

    <p>Choose Mood</p>
    <?php echo osu_list_terms_dd('moods', 'ibm_moods', '- choose a mood -'); ?>

    <p><em>BPM start</em></p>
    <select name="bpm_start">
        <option value="0">0</option>
        <option value="10">10</option>
        <option value="20">20</option>
        ... etc...
        <option value="220">220</option>
        <option value="230">230</option>
        <option value="240">240</option>
    </select>

    <p><em>BPM end</em></p>
    <select name="bpm_finish">
        <option value="0">0</option>
        <option value="10">10</option>
        <option value="20">20</option>
        ... etc...
        <option value="220">220</option>
        <option value="230">230</option>
        <option value="240">240</option>
    </select>

</form>

functions.php

// ADD CUSTOM QUERY VAR'S SO BPM WORKS BELOW
function osu_add_query_vars_filter( $vars ){
    $vars[] = "bpm_start";
    $vars[] = "bpm_finish";
    return $vars;
}
add_filter( 'query_vars', 'osu_add_query_vars_filter' );

// MODIFY THE MAIN QUERY OBJECT FOR TRACKS AND LOGOS CUSTOM POST TYPES
function osu_search_filter( $query ) {

    // POST ARCHIVE PAGE : Only for IBM tracks and Logos on search page
    if ( !is_admin() && isset($query->query_vars['post_type']) && is_search() ) {

        // Set vars
        $post_type  = $_GET['post_type'];
        $ibm_tracks = 'ibm_tracks';
        $ibm_logos      = 'ibm_logos';

        if(isset($post_type)) {

            // Only retrieve ibm_tracks
            // ------------------------------------------------------------------           
            if($post_type == $ibm_tracks) {

                // Set post type to ibm_tracks
                $query->query_vars['post_type'] = $ibm_tracks;

                // Allow url to alter the query
                $bpm_start  = $_GET['bpm_start'];
                $bpm_finish = $_GET['bpm_finish'];

                // Filter posts by BPM range
                if(isset($bpm_start) && isset($bpm_finish)) {

                    // Alter query with meta_query for BPM
                    $meta_query = array(
                        array(
                            'key'       => 'bpm',
                            'value'     => array($bpm_start, $bpm_finish),
                            'type'      => 'NUMERICAL',
                            'compare'   => 'BETWEEN'
                        )
                    );
                    $query->set( 'meta_query', $meta_query );

                }

            } // end if($post_type == ibm_tracks)

            // Only retrieve ibm_logos
            // ------------------------------------------------------------------
            if($post_type == $ibm_logos) {

                // Set post type to ibm_logos
                $query->query_vars['post_type'] = $ibm_logos;

            }

        } // end if(isset($post_type))

    } // end if ( !is_admin() etc. )

    return $query;

}
// Hook my above function to the pre_get_posts action
add_action( 'pre_get_posts', 'osu_search_filter' );
4
  • First of all, your code is extremely unsafe and a hacker's dream. I won't be surprised if your site gets hacked in the near future. Secondly, are you sure that your custom fields are correctly saved Jun 1, 2016 at 9:21
  • To identify if the problem is in the bpm $meta_query have you tried to change the comparison method? i.e. look for matching a specific value for $bpm_start only. If it works, then probably you should play with the bpm $meta_query declaration.
    – Capiedge
    Jun 1, 2016 at 9:23
  • @Peter Goosen, yes my 'bpm' custom fields are correctly saved as I'm using them on individual track pages. How might I improve the security of my code?
    – Osu
    Jun 1, 2016 at 9:41
  • @Capiedge, thanks for the reply. I tried matching the $bpm_start only and it returns the correct track even when selecting the genre and moody taxonomies, so I'm assuming my issue is with the comparison method... if I'm trying to search within a range, should I not be using 'between'?
    – Osu
    Jun 1, 2016 at 9:45

1 Answer 1

3

The type argument in your meta query is wrong. It spells NUMERICAL but the correct value is NUMERIC.

$meta_query = array(
    array(
        'key'       => 'bpm',
        'value'     => array($bpm_start, $bpm_finish),
        'type'      => 'NUMERIC',
        'compare'   => 'BETWEEN'
    )
);
5
  • That fixed it! Thanks so much - now I need to figure out why my code is so insecure as commented by @Pieter Goosen and sort that... web development is a never-ending loop of frustration sometimes...
    – Osu
    Jun 1, 2016 at 9:59
  • 1
    oops @cybmeta You are right! I will remove my answer, to avoid confusions!
    – Capiedge
    Jun 1, 2016 at 9:59
  • For securtiy, for example, you could transform the value of $_GET to int type, so it is always a integer number before you use it. Or maybe better, use add_rewrite_tag() to add the custom query strings and a regex to validate their values; then use get_query_var() instead of $_GET.
    – cybmeta
    Jun 1, 2016 at 10:07
  • Thanks @cybmeta that's great advice. Just wondering how you recommend I deal with $_GET['post_type']? Should I use something like sanitize_key( $_GET['post_type'] ) or is there a function that's better for that kind of thing? I'm going to read up on security a bit, sounds like I have a lot to learn...
    – Osu
    Jun 1, 2016 at 11:06
  • 2
    You can always use $post_type = filter_input( INPUT_GET, 'post_type', FILTER_SANITIZE_STRING ) to check if $_GET['post_type'] isset, and if set, sanitize it accordingly. The value of $post_type will either be false or the sanitized string with the post type value. For integers you can use $bpm_start = filter_input( INPUT_GET, 'bpm_start', FILTER_VALIDATE_INT ) Jun 1, 2016 at 12:34

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.