We had a client recently that needed to be able to create product filters based on the product categories they were assigned to. This presented a problem because we didn’t want to clog up the taxonomy / category pages with a bunch of filter options and the user also needed to be able to edit these categories in the future.
We took the approach of adding some ACF fields to the product categories and then inserting a meta box on the products based on their selected categories which would list the ACF fields as checkboxes, similar to selecting a taxonomy.
It is hard to explain so here is a bunch of code and images to explain.



Finally, here is the code needed to accomplish this.
All of this code goes into your “functions.php” file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
/** * Adds a metabox to the product pages to list the available filters * @link: https://wptheming.com/2010/08/custom-metabox-for-post-type/ */ add_action( 'add_meta_boxes', 'jb_add_filter_metaboxes' ); function jb_add_filter_metaboxes() { add_meta_box( 'jb_product_filters', 'Product Filters', 'jb_product_filters', 'product', 'side', 'default' ); } /** * Build the filter selection functionality * @link: https://wptheming.com/2010/08/custom-metabox-for-post-type/ */ function jb_product_filters() { global $post; $terms = wp_get_post_terms($post->ID, 'product_cat', array( 'fields' => 'ids' )); $filters = array(); foreach($terms as $term_id){ $filter = get_field('filters', 'term_'. $term_id); if(! empty($filter)){ $filters = array_merge($filters, $filter); } } if(! empty($filters)){ foreach($filters as $filter){ $meta_key = sanitize_title( $filter['filter_name'] ); $current_filters = get_post_meta( $post->ID, $meta_key, true ); if(! empty($current_filters)){ $current_filters = explode(',',$current_filters); }else{ $current_filters = array(); } echo '<p style="font-weight: bold; margin: 0;">'.$filter['filter_name'].'</p>'; echo '<ul id="product_filters" class="categorychecklist form-no-clear" style="margin: 0 0 20px;">'; if(! empty($filter['filter_options'])){ foreach($filter['filter_options'] as $option){ echo '<li class="wpseo-term-unchecked"> <label class="selectit"> <input value="'.$option['option'].'" '.(in_array($option['option'], $current_filters)?'checked="checked"':'').' type="checkbox" name="jb_filter['.$meta_key.'][]"> '.$option['option'].'</label> </li>'; } } echo '</ul>'; } } } /** * Save the filter information as meta_data that we can query later * @link: https://wptheming.com/2010/08/custom-metabox-for-post-type/ */ add_action( 'save_post', 'jb_save_filter_meta', 1, 2 ); function jb_save_filter_meta( $post_id, $post ) { if ( 'revision' === $post->post_type ) { return; } // Return if the user doesn't have edit permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { return $post_id; } // Now that we're authenticated, time to save the data. $filter_meta = $_POST['jb_filter']; // Cycle through the $filter_meta array. foreach ( $filter_meta as $key => $value ) : $value = implode(',',$value); if ( get_post_meta( $post_id, $key, false ) ) { // If the custom field already has a value, update it. update_post_meta( $post_id, $key, $value ); } else { // If the custom field doesn't have a value, add it. add_post_meta( $post_id, $key, $value); } if ( ! $value ) { // Delete the meta key if there's no value delete_post_meta( $post_id, $key ); } endforeach; } /** * Filter products based on submitted filters */ add_action( 'pre_get_posts', 'jb_filter_products' ); function jb_filter_products( $query ) { if(is_tax()){ if(! empty($_POST['psearch'])){ $query->set('s', $_POST['psearch'] ); } if(! empty($_POST['jb_filter'])){ $meta_query = array('relation' => 'AND'); foreach($_POST['jb_filter'] as $key => $filter){ foreach($filter as $value){ $meta_query[] = array( 'key' => $key, 'compare' => 'LIKE', 'value' => $value ); } } $query->set( 'meta_query', $meta_query ); } } } |
Here is an example of what the front end HTML might look like.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<form id="filter-form" action="<?php echo $_SERVER[REQUEST_URI]; ?>" method="post"> <div class="search-input"> <div class="input-group mb-3"> <input type="text" maxlength="30" class="form-control" id="basic-url" placeholder="Search" name="psearch" value="<?php echo $_POST['psearch']; ?>"> <div class="input-group-append"> <button type="submit" class="input-group-text" id="basic-addon2"><i class="far fa-search"></i></button> </div> </div> </div> <?php if(! empty($filters)): ?> <div class="filter-wrapper"> <?php foreach($filters as $filter){ $meta_key = sanitize_title( $filter['filter_name'] ); echo '<p style="font-weight: bold; margin: 0;">'.$filter['filter_name'].'</p>'; echo '<ul id="product_filters" class="categorychecklist form-no-clear" style="margin: 0 0 20px;">'; //'.(in_array($option['option'], $current_filters)?'checked="checked"':'').' if(! empty($filter['filter_options'])){ foreach($filter['filter_options'] as $okey => $option){ echo '<li> <input value="'.$option['option'].'" '.(is_array($_POST['jb_filter'][$meta_key]) && in_array($option['option'], $_POST['jb_filter'][$meta_key])?'checked="checked"':'').' id="'.$meta_key.'-'.$okey.'" type="checkbox" class="awesomebox" name="jb_filter['.$meta_key.'][]"><label for="'.$meta_key.'-'.$okey.'" class="selectit">'.$option['option'].'</label> </li>'; } } echo '</ul>'; } ?> </div> <?php endif; ?> </form> |