How is it possible to display an "All" (=show all items) link in facets? Ideally with the total number of matching search results?

Comments

danielnolde’s picture

Version: 7.x-2.x-dev » 7.x-1.x-dev
cpliakas’s picture

Category: feature » support

I think it is possible, but this would be handled by the backend. Not sure if Solr provides this option out of the box, but you could add an alter callbacks function to each facet to explicitly add an "All" item with whatever query will show all facets.

danielnolde’s picture

Thanks Chris for the assessment.
Actually i just figured out a nice way to add an "All" link (without any resultcount, however) by changing the facet item build, either by implementing a custom facet filter class, or by using facetapi_bonus and implementing its hook_facet_items_alter() with the following code:


  // Prepare data for "All" link
  $firstitem = reset($build);
  $path = $firstitem['#path'];
  $query = array('f' => array());
  foreach($firstitem['#query']['f'] as $key => $value) {
    if (strpos($value, $settings->facet) !== 0) {
      $query['f'][] = $value;
    }
  }

  // ... other facet item alterations ... like

  // Add "All" link.
  $build['all'] = array(
                          '#markup' => t('All'),
                          '#path' => $path,
                          '#query' => $query,
                          '#html' => FALSE,
                          '#indexed_value' => '0',
                          '#active' => $facet_active_count ? 0 : 1,
                          '#item_parents' => array(),
                          '#item_children' => array(),
                          //'#count' is omitted to indicate to overridden
                          // theme function not to render count or removal-link
                          );

A bit of custom/overridden theme functions for link widget rendering bring the icing to the cake.

cpliakas’s picture

Thanks for posting your solution! This looks like a pretty cool workaround that is backend agnostic. My only concern would be if the "hard limit" was reached, in that case you would only be filtering the facets that were returned.

Just to cross post, there is some relevant information for Solr at #738076: Implement "All" Facet to facet block configuration.

Thanks again for posting,
Chris

danielnolde’s picture

> if the "hard limit" was reached, in that case you would only be filtering the facets that were returned.

hm, not sure if i understand you correctly, but there's no "filtering" of facet items in the code above, there's just the faux-item "All" added, whose query parameters are filtered to _not_ contain facet items of the facet it is added to. I can't see a.t.m. what problems that could cause in conjunction with hitting the hard limit for facet items...

cpliakas’s picture

Status: Active » Fixed

Gotcha. That makes sense to me. Marking support request as fixed.

iajay’s picture

I tried the above code and it was working correctly. But it does not work after installing this module http://drupal.org/project/facetapi_pretty_paths
When the hook_facet_items_alter is called the path of the facets is already changed and the #query is also null
Can you help me in fixing this?
Can we somehow call the hook_facet_items_alter before the paths are changed?
Kind regards
Ajjaykummar

iajay’s picture

I was able to solve this with the code. Sharing the code if anybody needs that

function mymodule_facet_items_alter(&$build, &$settings) {
if ($settings->facet == "field_tax_color") {
  if (module_exists('facetapi_pretty_paths')) {
    $firstitem = reset($build);
    $query = array('f' => array());
    $path = explode('/',$firstitem['#path']);
    for($i=1;$i<count($path);$i++) {
      if($path[$i]=="Color") { // where Color is the alias for the facet. I am not able to programatically get this from the settings array
        unset($path[$i]);
        unset($path[++$i]);
      }
    }
  $path = implode('/',$path);
  } else {
   $firstitem = reset($build);
   $path = $firstitem['#path'];
   $query = array('f' => array());
   if(isset($firstitem['#query']['f'])){ 
   foreach($firstitem['#query']['f'] as $key => $value) {
    if (strpos($value, $settings->facet) !== 0) {
      $query['f'][] = $value;
    }
   }
   }

  }
// Add "All" link.
  $build['all'] = array(
                          '#markup' => t('View All'),
                          '#path' => $path,
                          '#query' => $query,
                          '#html' => FALSE,
                          '#indexed_value' => '-1',
                          '#active' => 1,
                          '#item_parents' => array(),
                          '#item_children' => array(),
                          '#count' => -1,
                          );


 }
}

Let me know if there is any problem in this and how I can retrieve the alias for the facet.
Kind regards
Ajjaykummar

cpliakas’s picture

Thanks for posting, iajay!

Just a little tip, would you mind wrapping your code in <?php ?> tags so your snippet is easier to read?

Thanks,
Chris

iajay’s picture

added the
do let me know if you find a problem
Thanks
Ajjaykummar

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

sokrplare’s picture

This is available in the bonus module: http://drupal.org/project/facetapi_bonus

nick_vh’s picture

Status: Closed (fixed) » Active

covenantd, can you explain how this is available? I grepped for all and view all but I could not find a reference to this?

doliveros’s picture

If I understand #3 & #8 correctly, the code works more like a reset facet link, instead of a "select all" link.

This isn't what is expecting if, for example, you have a "Gifts" taxonomy with different gift types to filter in, and you want to filter out all products that doesn't contain any Gift taxonomy. Clicking on the -All- filter would reset the filter and display all the products (even if they are not classified as gifts), not the products containing any of the gift taxonomies (All gift products).

Any thoughts on this one?

peterx’s picture

#14 is right. I expect the All option to select all the categories displayed below the all.
Select abc:
* All
* a
* b
* c
All should select a or b or c. X, y, and z might also exist but if they are not listed, they should not be included.

scoffield’s picture

mattiasj’s picture

This solution works great, but in my case I have several different facets. Category and year. With my custom "all", or in my case "reset" link they will clear the facets for all my blocks. Any ideas how to make the link only function for their own blocks?

drclaw’s picture

Just an update to the solution in Comment #8 where the pretty path alias is pulled dynamically:

/**
 * Implements HOOK_facet_items_alter()
 *
 * Hook is supplied by the facetapi_bonus module
 */
function HOOK_facet_items_alter(&$build, &$settings) {

  // We're going to just add an "all" link to every facet block. But you could
  // limit it by doing something like:
  // if ($settings->facet == "field_product_category") ... etc.

  // Set up some vars
  $firstitem = reset($build);
  $path = $firstitem['#path'];
  $query = array('f' => array());

  // Special Handling for pretty paths
  if (module_exists('facetapi_pretty_paths')) {
    // Get the adapter
    $adapter = facetapi_adapter_load($settings->searcher);
    // Load the facet object
    $facet = facetapi_facet_load($settings->facet, $settings->searcher);
    // Create the processor
    $processor = new FacetapiUrlProcessorPrettyPaths($adapter);
    // Pull the pretty path alias
    $pretty_paths_alias = $processor->getFacetPrettyPathsAlias($facet);
    // Explode the path
    $path = explode('/', $path);
    // Loop through the path parts
    for ($i=1; $i<count($path); $i++) {
      // If the current path part is the path alias
      if ($path[$i] == $pretty_paths_alias) {
        // We unset this part and the next one
        unset($path[$i]);
        unset($path[++$i]);
      }
    }
    // Rebuild the path
    $path = implode('/', $path);
  }
  else {
    // For not-so-pretty paths all we have to do is rebuild the query without
    // any of the facet items for this filter
    if (isset($firstitem['#query']['f'])) {
      foreach ($firstitem['#query']['f'] as $key => $value) {
        if (strpos($value, $settings->facet) !== 0) {
          $query['f'][] = $value;
        }
      }
    }
  }

  // Build the "All" link.
  $build['all'] = array(
    '#markup' => t('View All'),
    '#path' => $path,
    '#query' => $query,
    '#html' => FALSE,
    '#indexed_value' => '-1',
    '#active' => 1,
    '#item_parents' => array(),
    '#item_children' => array(),
    '#count' => -1,
  );
}
auraell’s picture

where do we put the code in comment 18?

drclaw’s picture

You can put it in a custom module or in template.php in your theme. Be sure to replace "HOOK" with your module name (or theme name if it's in your theme)

mmncs’s picture

StatusFileSize
new7.82 KB

When I add this code in a custom module I get a checked all link even when I use one of the filters?

madhusudanmca’s picture

Hi All,

I have done some modifications to code in comment #18 , as that code didn't worked for me.


<?php
/**
* Implements HOOK_facet_items_alter()
*
* Hook is supplied by the facetapi_bonus module
*/
function HOOK_facet_items_alter(&$build, &$settings) {

  // We're going to just add an "all" link to every facet block. But you could
  // limit it by doing something like:
  // if ($settings->facet == "field_product_category") ... etc.

  $is_all_link_active = 1;
  $i = 0;
  foreach ($build as $key => $val){
    if(!($val['#active']) || $val['#active'] == 0){
      $is_all_link_active = 0;
    }
    if($i==0){
      // Get the adapter
      $adapter = facetapi_adapter_load($settings->searcher);
      // Load the facet object
      $facet = facetapi_facet_load($settings->facet, $settings->searcher);
      // Create the processor
      $processor = new FacetapiUrlProcessorPrettyPaths($adapter);
      // Pull the pretty path alias
      $pretty_paths_alias = $processor->getFacetPrettyPathsAlias($facet);
      // Explode the path
      $path_arr =  explode('/', $val['#path']);
      // Filtering out facet options from current Facet   
      do {
        $array_key = array_search($pretty_paths_alias,$path_arr);
        if($array_key){
          unset($path_arr[$array_key]);
          unset($path_arr[++$array_key]);
        }
      } while (in_array($pretty_paths_alias, $path_arr));
      
      $curr_path = implode('/', $path_arr);
    }
    $srch_all_path .=  $pretty_paths_alias . '/' . $val['#indexed_value'] . '/';
    $i++;
  }
  $curr_serch_all_path = (!$is_all_link_active) ? $curr_path .'/' . $srch_all_path : $curr_path .'/';
  
	// Building the "All" link.
    $all['All'] = array(
        '#markup' => t('All'),
        '#path' => $curr_serch_all_path,
        '#html' => '',
        '#indexed_value' => 'All',
        '#active' => $is_all_link_active,
        '#item_parents' => array(),
        '#item_children' => array(),
        '#query' => array(),
    );

    $build = $all + $build;
}
?> 


mattiasj’s picture

Issue summary: View changes

I would like to have a unique class for the reset link. I tried some variations without success. Any ideas?

PMorris’s picture

Does this code still work? I tried it in a custom module and in my theme but it doesn't seem to do anything at all.
I have the Facet API Bonus module installed. Is there something I have to turn on that I missed?

woprrr’s picture

I suggest that this approach is more facet api way . Here I apply the theoretical facet how to use API, and this makes it possible not to have to recreate things that should not be done here as the URL processor (if needed requires more use $adapter->getUrlProcessor().

precisely this hook is invoked only once a facet with the active callback.

function HOOK_facet_items_alter(&$build, &$settings) {
  $adapter = facetapi_adapter_load($settings->searcher);
  $facet = facetapi_facet_load($settings->facet, $settings->searcher);
  $items = $adapter->getActiveItems($facet);

  $item_values = array();

  foreach ($items as $item) {
    $item_values[] = $item['value'];
  }

  $is_all_link_active = count($items) == 0;
  $all_link_path = $adapter->getFacetPath($facet, $item_values, TRUE);
  $all_link_query = $adapter->getQueryString($facet, $item_values, TRUE);


  $build['All'] = array(
    '#markup' => t('All'),
    '#path' => $all_link_path,
    '#html' => FALSE,
    '#count' => NULL,
    '#indexed_value' => 'All',
    '#active' => $is_all_link_active,
    '#item_parents' => array(),
    '#item_children' => array(),
    '#query' => $all_link_query,
  );
}
woprrr’s picture

Priority: Normal » Major
Issue tags: +Facet API, +Facet API integration

I think this issue do move to facetapi_bonus issue.

woprrr’s picture

Priority: Major » Normal
Status: Active » Closed (fixed)
nareshbw’s picture

Hi woprrr,

Thanks for your solution . It is working fine for me .

woprrr’s picture

@nareshbw

I'm glad that I served you :)

nareshbw’s picture

@woprrr

This code can be used if I want to reset filters from same block . But if I want to reset all filters from all blocks then this code is not working . Kindly help/suggest.

nareshbw’s picture

I changed the path and it fulfilled my requirement

$firstitem = reset($build);
$path = $firstitem['#path'];

$path = explode('/', $path);
// Loop through the path parts

$path = $path['0']."/".$path['1']."/".$path['2'];

knalstaaf’s picture

I've tried the code from #18, #22 and #25 in my template.php but with no result. Also tried the Facet API Bonus module (as mentionned in #12), but it's not clear to me either how that module offers a solution for this.

I have a vocabulary containing more that 50 items, so that url starts to look quite exotic with every term in there. If I could use a URL with such as myvocabulary/All (built with Facet API Pretty Paths), it would look a lot healthier.

(BTW: I'm not using Solr, but Search API Database Search)

marcoka’s picture

@knalstaaf facet api bonus offers a setting that you can use hook_facet_items_alter
here: http://root.artwaves.de/sharex/2016-02-04_13-33-39.png

my goal is also to have a link on a facet that shows all/ like skip this filter. my only problem is that i dont know what param the api needs to "show all/skip"

#2662018: Skip facet link / ignore filter / show all

#25

This code adds an "all" link to facets. But it is also selected by default: http://root.artwaves.de/sharex/2016-02-04_13-36-47.png

The code also adds the all link so that it like resets all filters

ray17n’s picture

#25 by woprrr works for me. Thanks.

igasi’s picture

#25 works perfectly to me.

I only added this code for collect all results and displayed in the filter.

// Calculate all results to All filter count.
  $total_counts = 0;
  foreach ($build as $item) {
    $total_counts += $item['#count'];
  }

// And set '#count' => $total_counts, on  $build['All']

Thanks woprrr

baldalo’s picture

#35 will work only if indexed entities belong exclusively to one item. If entities belong to multiple items, #35 will result in double counting them. Am I correct?

xlin’s picture

StatusFileSize
new2.25 KB

Added an option to add an "all" link, tested with Solr + type and language filter.

xlin’s picture

Forgot to add that code from #37 are mostly from search_api_glossary module.

qandeel’s picture

I have enhanced its functionality; with this patch, 'All' can be overridden by any string; also carry count and hide default missing value item.

i.e. "(none) (11)" will be replace by "(Overridden string) (11)"