Best way to create filters like Newegg or B&H Photo

What methods would be the best way to create a filtering sidebar similar to what sites like Newegg or B&H Photo have? I was looking at the Taxonomies extra, but the documentation page seems to be missing in action, and I’m not quite sure how to make it return a list of results from check boxes.

For an example of the kind of sidebar I’m talking about:

The extra Taxonomies has the snippet getPagesByTerm that returns the resources for one term only (property $term_id). If you want to filter by multiple terms, you have to write your own snippet (or extend the snippet getPagesByTerm). You also have to process $_POST or $_GET parameters from the frontend yourself. Additionally it seems that getPagesByTerm doesn’t support template variables.

Maybe you can use the extra Tagger instead for your purposes (if you don’t need hierarchically nested tags).

1 Like

I don’t really need the terms to be hierarchical, but I do need them to be in groups and preferably be predefined in the back end where the creators can check the matching terms rather than make things up on the fly. I liked the discoverability of terms in the backend with Taxonomies. If someone creates a new term in the back end it also needs to update the front end collection of checkboxes.

Most e-commerce stores use “faceted search”, which is what you have shown in your example photos. This differs from standard filtering by the ability to offer further arrays of filters depending on previous choices. If this is the complexity you’re needing then I think you will need to look at one of the MODX search extras like SimpleSearch or AdvSearch, combined with Tagger (probably). If you do a search for posts on those extras you will find some discussions about this already. And the MODX docs have a short article on SimpleSearch faceted search here:


If you don’t need the faceted aspect and just want a list of grouped filters, you can use Tagger to do this.

I had seen the faceted search example, because I have SimpleSearch setup, however (unless I missed something) I ruled that out because it looks like you have to setup a new PostHook for every filter term you add. If a manager wants to add a new term every coulple weeks while building out the site that becomes too complicated.

On the backend at least, Taxonomies seems to work perfectly like they want because a manager can just go create a new taxonomy term in the resource tree and it automatically shows up on every resource for any creator to checkbox that term.

The problem of course has been getting those same terms to be checkbox filters on the front end for anyone searching for things. And preferably to have it automatic whenever a term is added to the tree. I figured getResources could probably handle that, eventually.

I’m about to take another look at Tagger again to see if it can be clamped down a little to act similarly to Taxomomies

Not positive, but I believe with any of the tagging extras you will only be able to match one term at a time. So, if you want to be able to allow multiple checked filters then you will need one of the search extras… I think you can get Tagger and AdvSearch working together (I saw an article somewhere), so that might allow the backend ease you want with the frontend requirements. Maybe. Hopefully someone who has implemented this will pop in to help.

With the extra Tagger you can also search for multiple tags. The problem is, that with TaggerGetResourcesWhere you either get the resources that match at least one of the specified tags or the resources that match all of them ( &matchAll=`1` ). But what you probably want, is a behaviour like the one described in this question:


To achieve that, you’ll have to write your own snippet.

There’s a way, using Tagger, to search for multiple tags from the frontend? As in, render checkboxes that the user can check one or many?

Here is a simple example with Tagger that uses a custom snippet (as a replacement for TaggerGetResourcesWhere) to allow for the mixing of AND and OR (as described in my last post).

I created two groups in Tagger:

  • Group “color” (Id=1) with the tags “red”, “green” and “blue”
  • Group “size” (Id=2) with the tags “small”, “medium” and “large”

Template

[[!SetSubquery]]

<form method="post" action="[[~[[*id]]]]" id="my_filter_form">
    <h3>Color</h3>
    [[TaggerGetTags? &groups=`1` &rowTpl=`tplTagCheckbox`]]
    <h3>Size</h3>
    [[TaggerGetTags? &groups=`2` &rowTpl=`tplTagCheckbox`]]
    <input type="submit" value="Filter data">
</form>
<hr>
[[!getResources? &where=`[[!+filter_subquery]]`
    &parents=`1`
    &depth=`0`
    &limit=`0`
    &tpl=`@INLINE <h2>[[+pagetitle]] ([[+id]])</h2>`
]]

Set the property &parents to the right value.

Chunk "tplTagCheckbox"

<label for="filter-tag-[[+id]]"><input type="checkbox" id="filter-tag-[[+id]]" name="filter[[+group_id]][]" value="[[+id]]" [[!+filter_tag_ids:FormItIsChecked=`[[+id]]`]]/>[[+tag]]</label>

This chunk uses the snippet FormItIsChecked from the extra FormIt.

Snippet "SetSubquery"

<?php
$tagger = $modx->getService('tagger','Tagger',$modx->getOption('tagger.core_path',null,$modx->getOption('core_path').'components/tagger/').'model/tagger/',$scriptProperties);
if (!($tagger instanceof Tagger)) return '';

$group_ids = array(1,2); //The groups to include in the filtering
$all_checked_tags = array();
$where = array();
foreach($group_ids as $group_id){
    if (isset($_POST['filter'.$group_id])) {
        $tag_ids = $_POST['filter'.$group_id];
        if (is_array($tag_ids)){
            $tag_ids = array_map('intval', $tag_ids);
            $where[] = "EXISTS (SELECT 1 FROM {$modx->getTableName('TaggerTagResource')} r WHERE r.tag IN (" . implode(',',$tag_ids) . ") AND r.resource = modResource.id)";
            $all_checked_tags = array_merge($all_checked_tags,$tag_ids);
        }
    }
}
//This placeholder is used to preserve the "checked"-state of the checkboxes
$modx->setPlaceholder('filter_tag_ids',$modx->toJson($all_checked_tags));
//This placeholder is used for the getResources subquery
$modx->setPlaceholder('filter_subquery',$modx->toJSON($where));
return '';

This example uses the id of the tags instead of the alias to keep the code simple.

Similar code could be used for the extra Taxonomies. That extra has the database table tax_page_terms that it basically the same as the table modx_tagger_tag_resources from Tagger.

1 Like