0

I have two dropdowns on my frontpage. The first dropdown selects a custom post type, and the second selects a location. The second dropdown is filtered based on the selection of the first dropdown (ie, only showing locations that contain the selected cpt). The problem is, when an option in the first dropdown is selected, it takes very long to load the second dropdown options. If someone could have a look at my code and possibly help me figure out what is causing the delay, I will be greatly appreciative! (Below is the code from my functions.php file and front-page.php)

Functions.php:

function my_dropdown_categories( $taxonomy, $current_selected = '', $include = null ) {
// Get all terms of the chosen taxonomy
$terms = get_terms($taxonomy, array('orderby' => 'name'));

// our content variable
$list_of_terms = '<select id="location"  class="selectboxSingle" name="location">';

if ( ! is_wp_error( $terms ) ) foreach($terms as $term){

    // If include array set, exclude unless in array.
    if ( is_array( $include ) && ! in_array( $term->slug, $include ) ) continue;

    $select = ($current_selected == $term->slug) ? "selected" : ""; // Note: ==

    if ($term->parent == 0 ) {

        // get children of current parent.
        $tchildren = get_term_children($term->term_id, $taxonomy);

        $children = array();
        foreach ($tchildren as $child) {
            $cterm = get_term_by( 'id', $child, $taxonomy );
            // If include array set, exclude unless in array.
            if ( is_array( $include ) && ! in_array( $cterm->slug, $include ) ) continue;
            $children[$cterm->name] = $cterm;
        }
        ksort($children);

        // OPTGROUP FOR PARENTS
        if (count($children) > 0 ) {
        //     $list_of_terms .= '<optgroup label="'. $term->name .'">';
             if ($term->count > 0)
                 $list_of_terms .= '<option class ="group-result" value="'.$term->slug.'" '.$select.'>' . $term->name .' </option>';
        } else
            $list_of_terms .= '<option value="'.$term->slug.'" '.$select.'>'. $term->name .' </option>';
        //$i++;

        // now the CHILDREN.
        foreach($children as $child) {
             $select = ($current_selected == $child->slug) ? "selected" : ""; // Note: child, not cterm
             $list_of_terms .= '<option value="'.$child->slug.'" '.$select.'>'. $child->name.' </option>';

        } //end foreach

        if (count($children) > 0 ) {
            $list_of_terms .= "</optgroup>";
        }
    }
}

$list_of_terms .= '</select>';

return $list_of_terms;
}

add_action( 'wp_ajax_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );
add_action( 'wp_ajax_nopriv_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );

function wpse158929_get_terms_for_cpt() {
$ret = array( 'html' => '', 'error' => false );

if ( ! check_ajax_referer( 'wpse158929_get_terms_for_cpt_submit_', 'nonce', false /*die*/ ) ) {
    $ret['error'] = __( 'Permission error', 'wpfm' );
} else {
    $post_type = isset( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : '';
    $taxonomy = isset( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : '';
    $current_selected = isset( $_REQUEST['current_selected'] ) ? $_REQUEST['current_selected'] : '';

    if ( ! $post_type || ! $taxonomy ) {
        $ret['error'] = __( 'Params error', 'wpfm' );
    } else {
        global $wpdb;
        $sql = $wpdb->prepare( 'SELECT t.slug FROM ' . $wpdb->terms . ' t'
            . ' JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tt.term_id = t.term_id'
            . ' JOIN ' . $wpdb->term_relationships . ' AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id'
            . ' JOIN ' . $wpdb->posts . ' AS p ON p.ID = tr.object_id'
            . ' WHERE tt.taxonomy = %s AND p.post_type = %s AND p.post_status = %s'
            . ' GROUP BY t.slug'
            , $taxonomy, $post_type, 'publish' );
        $include = $wpdb->get_col($sql);
        $ret['html'] = preg_replace( '/<\/?select[^>]*>/', '', my_dropdown_categories( $taxonomy, $current_selected, $include ) );
    }
}

wp_send_json( $ret );
}

Front-page.php

<form action="<?php bloginfo('url'); ?>" method="get">
<?php
//$post_type = get_post_type_object( get_post_type($post) ); // GET SINGULAR NAME
$args = array(
'public'   => true,
'_builtin' => false
);
$output = 'objects'; // names or objects, note names is the default
$operator = 'and'; // 'and' or 'or'
$post_types = get_post_types($args, $output, $operator);
ksort($post_types);
echo '<select data-placeholder="I need a:" class="fade-in three selectboxSingle" name="post_type">';
foreach ( $post_types as $post_type ) {
$exclude = array('custom_type','shelf');
if(TRUE === in_array($post_type->name,$exclude)){
continue;
}
// Note: I think you need to use query_var here, rather than slug.
echo '<option value="'.$post_type->query_var.'">' . ucfirst($post_type->labels->singular_name) . '</option>';
}
echo "</select>";
?>

<?php 
// Set your custom taxonomy
$taxonomy = "location";
// Factored out taxonomy dropdown into function my_dropdown_categories() in "functions.php".
echo my_dropdown_categories( $taxonomy );
?>

<button class="submit-button alt" type="submit">Search</button>

<script type="text/javascript">
  jQuery(document).ready(function($) {
    $('select[name="post_type"]').change(function (event) {
    $("#location").prop("disabled", false);
    $('.submit-button').prop('disabled', false);
    $('.submit-button').removeClass("disabled");
    $('select[name="location"]').html("<option value=''>Loading...</option>");
    $(".selectboxSingle").trigger("chosen:updated");
      if($('select[name="post_type"]').val() === 'book'){
      $('#location').prop('disabled', true);
    }
      $.post("<?php echo admin_url('admin-ajax.php'); ?>", {
      action: 'wpse158929_get_terms_for_cpt',
      post_type: $(this).val(),
      taxonomy: <?php echo json_encode( $taxonomy ); ?>,
      current_selected: $('select[name="location"]').val(),
      nonce: <?php echo json_encode( wp_create_nonce( 'wpse158929_get_terms_for_cpt_submit_' ) ); ?>
    }, function( response ) {
    if ( response && !response.error ) {
    $('select[name="location"]').html(response.html);
    $("#location-dropdown").prop("disabled", false);
    $(".selectboxSingle").trigger("chosen:updated");
    }
    }, 'json'
    );
});

// Remove if you don't want to call change immediately.
$('select[name="post_type"]').change();
$('select[name="location"]').html("<option>Your location:</option>");
$('.submit-button').prop('disabled', true);
$("#location").prop("disabled", true);
$('.submit-button').addClass("disabled");
    if ($('select[name="post_type"]').val() !== '') {
        $('.submit-button').prop('disabled', false);
        $('.submit-button').removeClass("disabled");
        $("#location").prop("disabled", false);
        }
    });

4

I'm surprised it's all that slow but you can remove all but one of the database calls & most of the looping by passing the term data instead of just slugs to my_dropdown_categories() so get_terms() doesn't have to be called, plus a few other improvements (see comments) eg:

    function my_dropdown_categories( $taxonomy, $current_selected = '', $terms = null, $no_select = false ) {
    // If all terms ever needed, uncomment following line.
    //if ( $terms === null ) $terms = get_terms($taxonomy, array('orderby' => 'name'));

    // our content variable - use array (& join at end) for efficiency
    $ret = array();

    if ( ! $no_select ) { // If not required, don't wrap in select.
        $ret[] = '<select id="location"  class="selectboxSingle" name="location">';
    }

    if ( $terms && ! is_wp_error( $terms ) ) {
        $parents = $children = array();
        foreach($terms as $term){
            if ($term->parent == 0 ) {
                $parents[] = $term;
            } else {
                $children[$term->parent][] = $term;
            }
        }

        foreach($parents as $term){
            $select = ($current_selected == $term->slug) ? "selected" : ""; // Note: ==

            if ( empty( $children[$term->term_id] ) ) {
                $ret[] = '<option value="'.$term->slug.'" '.$select.'>'. $term->name .' </option>';
            } else {
                //$ret[] = '<optgroup label="'. $term->name .'">';
                $ret[] = '<option class="group-result" value="'.$term->slug.'" '.$select.'>'. $term->name.' </option>';
                foreach ( $children[$term->term_id] as $child ) {
                     $select = ($current_selected == $child->slug) ? "selected" : "";
                     $ret[] = '<option value="'.$child->slug.'" '.$select.'>'. $child->name.' </option>';
                }
                //$ret[] = "</optgroup>";
            }
        }
    }

    if ( ! $no_select ) {
        $ret[] = '</select>';
    }

    return implode( "\n", $ret );
}

add_action( 'wp_ajax_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );
add_action( 'wp_ajax_nopriv_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );

function wpse158929_get_terms_for_cpt() {
    $ret = array( 'html' => '', 'error' => false );

    if ( ! check_ajax_referer( 'wpse158929_get_terms_for_cpt_submit_', 'nonce', false /*die*/ ) ) {
        $ret['error'] = __( 'Permission error', 'wpfm' );
    } else {
        $post_type = isset( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : '';
        $taxonomy = isset( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : '';
        $current_selected = isset( $_REQUEST['current_selected'] ) ? $_REQUEST['current_selected'] : '';

        if ( ! $post_type || ! $taxonomy ) {
            $ret['error'] = __( 'Params error', 'wpfm' );
        } else {
            global $wpdb;
            // Get bare minimum of required data from database.
            $sql = $wpdb->prepare( 'SELECT t.term_id, t.name, t.slug, tt.parent FROM ' . $wpdb->terms . ' t'
                . ' JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tt.term_id = t.term_id'
                . ' JOIN ' . $wpdb->term_relationships . ' AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id'
                . ' JOIN ' . $wpdb->posts . ' AS p ON p.ID = tr.object_id'
                . ' WHERE tt.taxonomy = %s AND p.post_type = %s AND p.post_status = %s'
                . ' GROUP BY t.slug'
                . ' ORDER BY t.name'
                , $taxonomy, $post_type, 'publish' );
            $terms = $wpdb->get_results( $sql );
            $ret['html'] = my_dropdown_categories( $taxonomy, $current_selected, $terms, true /*no_select*/ );
        }
    }

    wp_send_json( $ret );
}

Also you probably don't need the call $('select[name="post_type"]').change(); in the javascript seeing as you're overwriting the select immediately afterwards...

3
  • 1
    Hey there, thank you so much for your response. I implemented your changes - it really made a huge difference!! Thanks so much, I REALLY appreciate your time and effort in helping me out - you made my day :) – Guit4eva Jan 9 '15 at 14:06
  • 1
    Hi Guit4eva thanks for the thanks! The answer can be improved further by passing all the data needed to my_dropdown_categories() so it doesn't have to call get_terms(), plus using an array instead of a looped concatenated string, plus not wrapping in select unless required, so I'll update the answer... – bonger Jan 10 '15 at 17:43
  • How on earth do you get so good at this?:) Thanks so much man, I was astounded by your first answer. Coming back and still improving it further literally made me fall off my chair!! Thanks so, so much, I seriously appreciate it a lot!!! – Guit4eva Jan 10 '15 at 20:42

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.