Conditional two level dropdown filter for custom post type

Like I said in the comment, this can be solved by using two dropdowns. Additionally I already hinted you to an answer of mine doing this for a simple, one level, non conditional category selection. Category dropdowns can easily constructed with wordpress by making use of wp_dropdown_categories.

To make it work as actual selection handed over to a query the dropdown has to be part of a form. This way the variable constructed according to the form method can be used for the query to be performed. I’ve chosen POST as method here, GET would be the other possibility, take a look at my linked answer for a little bit more information on that. The method how the form is constructed is an adaptation of this example from the wp_dropdown_categories() codex page. Key here is to disable echoing and to adapt the select output. By doing it this way we can have a button less form submit, but, if no javascript is available, we have a fallback to show the button – the button is wrapped into noscript tags.

For this setup several steps are necessary. The solution below operates with a hierarchical taxonomy to distinguish between regions and areas – in case of a custom taxonomy you have to make use of the taxonomy parameter. In a first step, we wanted to make sure that the regions dropdown only shows the top level in the hierarchy, this is done with the parameter hierarchical and depth. Additionally there are conditional states for the cases »no option selected« and »option has been selected« – parameters show_option_none and selected, dependent on $_POST['region'].

The second, area dropdown gets only shown if a region was selected before. It wouldn’t be useful otherwise anyways, because its content is determined by the first, region dropdown. For this the child_of parameter is brought to use, of course bringing this back to the selected region saved in $_POST['region']. Another thing to do is making sure we keep the information about the region, for this the hidden input is part of the second form. The latter assures that we loop through the region as long as we’re just changing the areas.

The last part of the setup takes care of conditionally giving back the content. for one we want to make sure to show a post only if a region and area is selected. If that’s the case the correct post is determined by making use of the cat parameter of WP_Query. Like shown below it’s possible to use the conditions to show additional information for the user.

Code:

Part 1:

    <form method="POST" action="">
        <div>
            <?php
                $args = array(
                    // hierarchical is needed to define depth
                    'hierarchical' => 1,
                    // regions are the top level in a hierarchical taxonomy
                    'depth' => 1,
                    'orderby' => 'name',
                    // we're not echoing, because we want to construct a no button solution
                    'echo' => 0,
                    'taxonomy' => 'regions',
                    // this leads to variable name $_POST['region']
                    'name' => 'region'
                );
                if( ! isset($_POST['region']) ):
                    // if no region was selected prior we show this by default
                    $args['show_option_none'] = 'Select Region';
                else:
                    // otherwise make sure the region form shows what was selected before
                    $args['selected'] = $_POST['region'];
                endif;
                // we're putting the dropdown output into a variable
                $region = wp_dropdown_categories( $args );
                // this enables the buttonless js possibility
                $region = preg_replace("#<select([^>]*)>#", "<select$1 onchange="return this.form.submit()">", $region);
                // now echo the dropdown output
                echo $region;
                // the »<noscript>...</noscript> part makes sure there is a fallback in case there is no js
            ?>
            <noscript>
                <div>
                    <input type="submit" value="region" />
                </div>
            </noscript>
        </div>
    </form>

Part 2:

    <?php
        // the area dropdown is only shown if a region was selected
        if( isset($_POST['region']) && $_POST['region'] ):
    ?>
        <form method="POST" action="">
            <?php // we add a hidden input to hand over the region selected ?>
            <input type="hidden" name="region" value="<?php echo $_POST['region'] ?>">
            <div>
                <?php
                    $args = array(
                        // the areas to show are children of the prior selected region
                        'child_of' => $_POST['region'],
                        'hide_if_empty' => true,
                        'orderby' => 'name',
                        'echo' => 0,
                        'taxonomy' => 'regions',
                        'name' => 'area'
                    );
                    if( ! isset($_POST['area']) ):
                        $args['show_option_none'] = 'Select Area';
                    endif;
                    $area = wp_dropdown_categories( $args );
                    $area = preg_replace("#<select([^>]*)>#", "<select$1 onchange="return this.form.submit()">", $area);
                    echo $area;
                ?>
                <noscript>
                    <div>
                        <input type="submit" value="area" />
                    </div>
                </noscript>
            </div>
        </form>
    <?php endif; ?>

Part 3:

    <?php
        // we're only performing a query if both a region and an area have been selected
        if( isset($_POST['region']) && isset($_POST['area']) && $_POST['region'] && $_POST['area'] ):
            $args = array( 
                'post_type' => 'local_services',
                // this assures the post is selected by the area selected
                'cat' => $_POST['area'],
                'posts_per_page' => 1 
            ); 
            $loop = new WP_Query( $args ); 
            while ( $loop->have_posts() ) : $loop->the_post(); 
                the_title();
                echo '<div class="entry-content">'; 
                    the_content(); 
                echo '</div>'; 
            endwhile;
        // show conditional information according to the step we're at
        elseif( isset($_POST['region']) && ! isset($_POST['area']) && $_POST['region'] ):
            echo 'Please select an area';
        else:
            echo 'Please select a region';
        endif;
    ?>

I only divided this into parts to make it a little bit better readable, those three parts actually are seamlessly together. This can be used directly in your according template, or a function could be constructed of this, if you want to keep your templates clean. That’s about it. As you have seen, I commented the code too, so, with above explanation, the procedure should be pretty clear. Take a look at the according documentations or do a search on here to further customize it to your needs.


Note: I’ve tested this and running similar to this, so I’m not sure what the problem the OP has been experiencing was. As far as I’m concerned, this is working.


Edit: in response to comment

Are you debugging? You might have conflicting query vars here, because your taxonomy name is actually »region« not »regions«. Instead of using

'query_var' => true

you could try

'query_var' => 'regions'

Or you give the form another name then region, do not forget to change all the related $_POST variables accordingly.

Leave a Comment