1
votes

I have products as a CPT in Wordpress, and am trying to filter posts based on taxonomies with checkboxes. I have two taxonomies, brands and sizes. They are outputted like this in the index.php file:

    <form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">  
    <?php    
    if( $brands = get_terms( array( 'taxonomy' => 'brand' ) ) ) :
    foreach( $brands as $brand ) :
    echo '<p><input type="checkbox" id="brand_' . $brand->term_id . '" name="brand_' . $brand->term_id . '" /><label for="brand_' . $brand->term_id . '">' . $brand->name . '</label></p>';
    endforeach;
    endif;  
    ?>

    <?php  
    if( $sizes = get_terms( array( 'taxonomy' => 'sizes' ) ) ) :
    foreach( $sizes as $size ) :
    echo '<p><input type="checkbox" id="size_' . $size->term_id . '" name="size_' . $size->term_id . '" /><label for="size_' . $size->term_id . '">' . $size->name . '</label></p>';
    endforeach;
    endif;  
    ?>
    <button>Apply filter</button>
    <input type="hidden" name="action" value="myfilter">
    </form>

In my functions.php file I have the following to build the WP_query and include the taxonomies:

function misha_filter_function(){
$args = array(
    'orderby' => 'date',
    'post_type' => 'clothing_product',
    'posts_per_page' => -1
);


//brand checkboxes
if( $brands = get_terms( array( 'taxonomy' => 'brand' ) ) ) :
$all_terms = array();

foreach( $brands as $brand ) {
    if( isset( $_POST['brand_' . $brand->term_id ] ) && $_POST['brand_' . $brand->term_id] == 'on' )
         $all_terms[] = $brand->slug;
}

if( count( $all_terms ) > 0 ) {
    $args['tax_query'] = array(
        array(
            'taxonomy' => 'brand',
            'field' => 'slug',
            'terms'=> $all_terms
        )
    );
}
endif;

//sizes checkboxes
if( $sizes = get_terms( array( 'taxonomy' => 'sizes' ) ) ) :
$all_terms = array();

foreach( $sizes as $size ) {
    if( isset( $_POST['size_' . $size->term_id ] ) && $_POST['size_' . $size->term_id] == 'on' )
         $all_terms[] = $size->slug;
}

if( count( $all_terms ) > 0 ) {
    $args['tax_query'] = array(
        array(
            'taxonomy' => 'sizes',
            'field' => 'slug',
            'terms'=> $all_terms
        )
    );
}
endif;

$query = new WP_Query( $args );

if( $query->have_posts() ) :
    while( $query->have_posts() ): $query->the_post();
        echo '<h2>' . $query->post->post_title . '</h2>';
    endwhile;
    wp_reset_postdata();
else :
    echo 'No posts found';
endif;
die();
}
add_action('wp_ajax_myfilter', 'misha_filter_function'); 
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');

And this is the AJAX/jQuery in scripts.js:

jQuery(function($){
$('#filter').submit(function(){
    var filter = $('#filter');
    $.ajax({
        url:filter.attr('action'),
        data:filter.serialize(), // form data
        type:filter.attr('method'), // POST
        beforeSend:function(xhr){
            filter.find('button').text('Processing...'); // changing the button label
        },
        success:function(data){
            filter.find('button').text('Apply filter'); // changing the button label back
            $('#response').html(data); // insert data
        }
    });
    return false;
});
});

What I have is a semi-working result where checkboxes are created for all available brands and sizes and filtering is possible. The problem is that if size XL and brand Nike are checked, it's pulling all products with size XL, even brands that aren't Nike, which is not what I want.

Looking at the Wordpress Codex,

tax_query takes an array of tax query arguments arrays (it takes an array of arrays). This construct allows you to query multiple taxonomies by using the relation parameter in the first (outer) array to describe the boolean relationship between the taxonomy arrays.

So I'm guessing the two taxonomies should be two separate arrays inside the tax_query array, but can that somehow be combined with the foreach loops?

    foreach( $brands as $brand ) {
    if( isset( $_POST['brand_' . $brand->term_id ] ) && $_POST['brand_' . $brand->term_id] == 'on' )
         $all_terms[] = $brand->slug;
}

Thanks a bunch

2

2 Answers

2
votes

Addition to Oscar's solution If anyone wants to determine relationship between taxonomies "AND" or "OR" based one how much taxonomies are selected: Here is my small tweaks:

if (empty($brands_terms) || empty($sizes_terms)) {
 $relation = 'OR';
}else{
 $relation = 'AND';
}

And in query arg write it like this;

'relation' => $relation,
2
votes

Here is how I got it working

Functions.php

function misha_filter_function(){   

//brands checkboxes
if( $brands = get_terms( array( 'taxonomy' => 'brand' ) ) ) :
$brands_terms = array();

foreach( $brands as $brand ) {
    if( isset( $_POST['brand_' . $brand->term_id ] ) && $_POST['brand_' . $brand->term_id] == 'on' )
         $brands_terms[] = $brand->slug;
}
endif;

//sizes checkboxes
if( $sizes = get_terms( array( 'taxonomy' => 'sizes' ) ) ) :
$sizes_terms = array();

foreach( $sizes as $size ) {
    if( isset( $_POST['size_' . $size->term_id ] ) && $_POST['size_' . $size->term_id] == 'on' )
         $sizes_terms[] = $size->slug;
}
endif;

$args = array(
    'orderby' => 'date',
    'post_type' => 'clothing_product',
    'posts_per_page' => -1,
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'brand',
            'field' => 'slug',
            'terms' => $brands_terms
        ),
        array(
            'taxonomy' => 'sizes',
            'field' => 'slug',
            'terms' => $sizes_terms
        )
    )
);

$query = new WP_Query( $args );

if( $query->have_posts() ) :
    while( $query->have_posts() ): $query->the_post();
        echo '<h2>' . $query->post->post_title . '</h2>';
    endwhile;
    wp_reset_postdata();
else :
    echo 'No posts found';
endif;

die();
}
add_action('wp_ajax_myfilter', 'misha_filter_function'); 
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');

Form

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">

        <div>
                <?php  
                      if( $brands = get_terms( array( 'taxonomy' => 'brand' ) ) ) :
                            echo '<ul class="brands-list">';
                        foreach( $brands as $brand ) :
                            echo '<input type="checkbox" class="" id="brand_' . $brand->term_id . '" name="brand_' . $brand->term_id . '" /><label for="brand_' . $brand->term_id . '">' . $brand->name . '</label>';
                            if ($brand !== end($brands)) { echo '<li class="list-spacer">/</li>'; }
                        endforeach;
                            echo '</ul>';
                    endif;
                ?>
            </div>

            <div>
                <?php  
                      if( $sizes = get_terms( array( 'taxonomy' => 'sizes' ) ) ) :
                            echo '<ul class="sizes-list">';
                        foreach( $sizes as $size ) :
                            echo '<input type="checkbox" class="" id="size_' . $size->term_id . '" name="size_' . $size->term_id . '" /><label for="size_' . $size->term_id . '">' . $size->name . '</label>';
                            if ($size !== end($sizes)) { echo '<li class="list-spacer">/</li>'; }
                        endforeach;
                            echo '</ul>';
                    endif;

                ?>
            </div>

        <button class="btn btn-primary">Filter</button>
        <input type="hidden" name="action" value="myfilter">
        </form>

Main thing was to have the tax query arrays in one place with relation => AND. Didn't change the jQuery/AJAX. Should be noted that this code does not "display all" if no boxes are checked.