Deselect an earlier selected filter which became inactive

Created on 9 January 2024, over 1 year ago

Problem/Motivation

When a selected filter becomes inapplicable for the narrowed down results set, it disappears from the facet block but retains in the URL. Under some circumstances it might lead to the empty results set. See the steps

Steps to reproduce

2 taxonomy facets, let's say:
- Color
- Size

"Test 1" node. Color - Red, Size - XL

  1. Select the Color to "Red" (1) and "Blue" (2)

    The parameters in the URL:

    https://site/catalog?
    f[0]=color:1 //Red
    &f[1]=color:2 //Blue
    
  2. Select the "XL" (3) Size
    The parameters in the URL:
    https://site/catalog?
    f[0]=size:3 //XL
    &f[1]=color:1 //Red
    &f[2]=color:2 //Blue
    

    RESULTS: Only the "Test 1" node found (color - Red, size - XL) and the "Blue" color disappears from the Color facet options (expected as there are no Blue products of the XL size). But it retains in the URL

  3. Deselect the "Red" color
    RESULT: No results found
    EXPECTED RESULT: The results for the XL size (not necessary tagged with Blue)

    The parameters in the URL:

    https://site/catalog?
    f[0]=size:3 //XL
    &f[1]=color:2 // Blue (should not be there)
    

The problem is that Blue (f[1]=color:2) stays in the URL despite it is inactive (not applicable to any search results)

Proposed resolution

I created a custom URL processor extending the default QueryString URL processor.


namespace Drupal\my_custom\Plugin\facets\url_processor;

use Drupal\facets\FacetInterface;
use Drupal\facets\Plugin\facets\url_processor\QueryString;

/**
 * My Query string URL processor.
 *
 * @FacetsUrlProcessor(
 *   id = "my_query_string",
 *   label = @Translation("My Query string"),
 *   description = @Translation("Query string + fix for removing inactive facets from the URL")
 * )
 */
class MyQueryString extends QueryString {

  /**
   * {@inheritdoc}
   */
  public function buildUrls(FacetInterface $facet, array $results) {
    $results = parent::buildUrls($facet, $results);

    // Collect the facet raw results.
    // TODO: have configurable set of00 facets to check against.
    $results_vals = [];
    foreach ($results as $result) {
      $results_vals[] = $result->getRawValue();
    }

    foreach ($results as &$result) {
      $url = $result->getUrl();
      $params = $url->getOption('query');

      foreach ($params[$this->filterKey] as $index => $param) {
        if (str_starts_with($param, $this->urlAlias)) {

          $filter_param_parts = explode(':', $param);
          $filer_val = $filter_param_parts[1];

          // If the filter presents in the URL but not in the facet results.
          if (!in_array($filer_val, $results_vals)) {
            unset($params[$this->filterKey][$index]);
          }

          $url->setOption('query', $params);
          $result->setUrl($url);
        }
      }
    }

    return $results;
  }

}

Remaining tasks

Need input from the community. We can consider patching the QueryString URL processor

📌 Task
Status

Active

Version

2.0

Component

Code

Created by

🇨🇦Canada klimp Montréal, QC

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @klimp
  • Hi,

    Thank you for the fix, it helped me.

    I had to add a few elements to make it work, i post them here.

    Fix warnings when parameters are empty

    foreach ($results as &$result) {
          $url = $result->getUrl();
          $params = $url->getOption('query');
    
          // FIX : we need to make sure the array exists
          $params[$this->filterKey] = $params[$this->filterKey] ?? [];
    
          foreach ($params[$this->filterKey] as $index => $param) {
    

    Override query_string when using block facets

    I couldn't select the url processor when using facets as blocks. This overrides the processor, which is forced to query_string by default.
    This could be further improved to allow dynamic selection of the url processor.

    Create a new facet Entity

    namespace Drupal\facet_override\Entity;
    
    class Facet extends \Drupal\facets\Entity\Facet {
    
      /**
       * {@inheritdoc}
       */
      public function getFacetSourceConfig() {
        $facet_source = parent::getFacetSourceConfig();
        $facet_source->set('url_processor', 'query_string_fix');
        return $facet_source;
      }
    

    Create a hook to use this entity

    (In facet_override.module)

    function facet_override_entity_type_alter(array &$entity_types) {
      // Add the facet_override entity type to the list of entity types.
      if (isset($entity_types['facets_facet'])) {
        $entity_types['facets_facet']->setClass('\Drupal\facet_override\Entity\Facet');
      }
    }
    
Production build 0.71.5 2024