Skip to main content

Example: Category filter

Lets say you want to extend the filter widget by adding an additional category filter. In this case we're adding a list of filter out of which the user can select a single category to filter on.

In order to extend the filter widget we need to do a couple of things:

  • Insert a filter hook onto the variants that actually takes the selected category and adds the correct filters to the variants query.
  • Add a new filter widget template called categories.php and add it to the widget-filters.php.
  • Add a function that returns all selectable categories as an ajax request.
  • Extend the javascript by adding the componented above to the filter widget.

Filter Hook

Part of your theme
function apply_categories_filters( $query_args ): array {
$category_id = $_POST['category'];
$action = $_POST['action'] ?? 'default';

if ( $action == 'eduframe_get_categories_filter' ) {
return $query_args;
}

if ( ! empty( $category_id ) ) {
global $wpdb;
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT eduframe_id_meta.meta_value AS eduframe_id FROM `$wpdb->posts` as post
LEFT JOIN `$wpdb->postmeta` AS eduframe_id_meta ON ( post.ID = eduframe_id_meta.post_id AND eduframe_id_meta.meta_key = '_eduframe_id' )
LEFT JOIN `$wpdb->postmeta` AS category_id_meta ON ( post.ID = category_id_meta.post_id AND category_id_meta.meta_key = '_eduframe_category_id' )
WHERE category_id_meta.meta_value = %d AND post.post_type = 'ef_product'",
$category_id
)
);

$query_args['meta_query'][] = array(
'key' => '_eduframe_product_id',
'value' => array_column( $results, 'eduframe_id' ),
);
}

return $query_args;
}

Which you can add like this:

add_filter( 'eduframe_variant_ajax_query_args', 'apply_categories_filters', 11, 1 );

Ajax Hook

Part of your theme
public static function get_categories_filter() {
ob_start();

global $selected;
if ( isset( $_POST['category'] ) ) {
if ( is_array( $_POST['category'] ) ) {
$selected = array_pop( $_POST['category'] );
} else {
$selected = $_POST['category'];
}
} else {
$selected = null;
}

$variants = new \WP_Query(
array(
'posts_per_page' => -1,
'post_status' => 'private',
'post_type' => 'ef_variant',
'tax_query' => array(
'relation' => ' AND ',
),
)
);

global $available;
$available = [];

// Get all products for these variants
while ( $variants->have_posts() ) {
$variants->the_post();
global $variant;

$available[] = $variant->get_product()->get_eduframe_category_id();
}

// Only unique values
$available = array_unique( $available );

$query_args = array(
'posts_per_page' => -1,
'post_status' => 'published',
'post_type' => 'ef_category',
'tax_query' => array(
'relation' => ' AND ',
),
);

global $categories;
$categories = new \WP_Query(
apply_filters( 'eduframe_category_filters_ajax_query_args', $query_args )
);

ef_get_template( 'filters/categories.php' );

die();
}

Which you can hook into Efurame like this:

add_action( 'wp_ajax_eduframe_get_categories_filter', 'get_categories_filter' );

Template

templates/filters/categories.php
<?php
global $categories;
global $selected;
global $available;

?>

<div class="filter-header" data-target="category-options">
<h2 class="filter-title"><?php _e( 'Category', 'eduframe' ); ?></h2>
</div>

<ul id='category-options' class="option-set" data-group="categories">
<?php
if ( $categories ) {
while ( $categories->have_posts() ) {
$categories->the_post();
global $category;

?>
<li class="nobullet">
<input type="checkbox"
value="<?= $category->get_eduframe_id(); ?>"
id="<?= $category->get_slug(); ?>"
<?php
if ( $selected && $category->get_eduframe_id() == $selected ) {
echo ' checked';
}
if ( ! in_array( $category->get_eduframe_id(), $available ) ) {
echo ' disabled';
}
?>
/>
<label class="filter-text"
for="<?php echo $category->get_slug(); ?>"
id="<?php echo $category->get_slug(); ?>">
<?php echo $category->get_name(); ?>
</label>
</li>
<?php
}
}
?>
</ul>

Javascript

// Async filtering script
$(document).ready(function() {
window.efScripts.onVariantsFilter.push(fetchCategoriesFilter);

fetchCategoriesFilter();
});

function bindClickHandler() {
$('#category-options :checkbox').on('click', function() {
const value = this.value;

if(this.checked) {
window.efScripts.variantFilters.category = value
} else {
window.efScripts.variantFilters.category = [];
}

window.efScripts.applyVariantFilters();
});
}

function fetchCategoriesFilter(){
jQuery.ajax({
type : 'POST',
url : efScripts.ajaxUrl,
data : {
'action' : 'eduframe_get_categories_filter',
...window.efScripts.variantFilters
},
timeout: 5000,
beforeSend: function(){
$('#category-options :checkbox').unbind('click');
},
success: function(response) {
jQuery('#categories-filter').html(response);
bindClickHandler();
},
error: function(xhr, textStatus, errorThrown){
jQuery('#categories-filter').html(xhr.responseText);
}
});
}