0

I'm on Drupal 9 and I have an AJAX view with a page display and an exposed filter block and a single filter exposed, "Search: Fulltext search".

The Search API module is configured to search inside 3 different content types: products, articles and authors.

I've also configured a facet to filter on the content type, using a widget type "links", that let filter one content type at time.

So, initially, the search page shows the results of all the three content types. After users filter a content type, they can switch only to another one and never return to the no content type chosen situation.

What I need to do is that the results are listed by default with one specific content type. If that content type yield no results, the following content type should be used as filter. If there are no result, just no result should be shown.

In other words, the no content type chosen situation should be never available.

I've tried to force the filter on content type, if missing, using hook_views_pre_view(), but it doesn't seem to work.

Actually, I've no idea if it is the correct approach or I have to work on different hook\somewhere else.

How do I solve this issue?

This is the hook code.

function my_module_views_pre_view(\Drupal\views\ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() === 'search' && $display_id === 'page_1') {
    $active_filters = $view->getExposedInput();
    if (isset($active_filters["search_api_fulltext"]) && !empty($active_filters["search_api_fulltext"])) {
      if (!isset($active_filters["f"])) {
        $active_filters["f"] = ['content_type:product'];
      }
      else {
        $content_type_filter_exists = FALSE;
        foreach ($active_filters["f"] as $facet_filter) {
          $filter_array = explode(':', $facet_filter);
          if ($filter_array[0] === 'content_type') {
            $content_type_filter_exists = TRUE;
            break;
          }
        }
        if (!$content_type_filter_exists) {
          $active_filters["f"] = array_merge($active_filters["f"], ['content_type:product']);
        }
      }
      $view->setExposedInput($active_filters);
    }
  }
  //$view->exposed_raw_input = array_merge($active_filters, (array) $view->exposed_raw_input);
}
0

Initially I tried some different approach:

  • extending SearchApiString
  • an event subscriber of KernelEvents::REQUEST altering the incoming url

because by default facets\search api rely on the query_params of the url and the "values" of the relative facets various objects of the modules. Both of those those approaches kinda failed, because the first didn't update the query params of the request, while the second caused some bugs that I couldn't manage to solve.

The working approach was to extending the default url_processor QueryString - which both initialises the active facets filters and build the url after a search.

Injecting the logic when the class is created allowed to do a minor modification without breaking all the standard functionality.

<?php

namespace Drupal\my_module\Plugin\facets\url_processor;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\facets\Plugin\facets\url_processor\QueryString;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Query string URL processor.
 *
 * @FacetsUrlProcessor(
 *   id = "my_query_string",
 *   label = @Translation("My Query string"),
 *   description = @Translation("Add description")
 * )
 */
class MyQueryString extends QueryString {

  /**
   * A string of how to represent the facet in the url.
   *
   * @var string
   */
  protected $urlAlias;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher) {
    //this is where I'm adding a default value
    $this->alterRequest($request);
    parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager, $eventDispatcher);
  }

  /**
   * @param \Symfony\Component\HttpFoundation\Request $request
   */
  private function alterRequest(Request $request): void {
    $query = $request->query;
    $facet_new_parameters = [];

    if ($facet_parameters = $query->get('f')) {
      $content_type_filter_exists = FALSE;

      foreach ($facet_parameters as $parameter) {
        $sepator = ':';
        $explosion = explode($sepator, $parameter);
        $facet_type = array_shift($explosion);
        if ($facet_type === 'content_type') {
          $content_type_filter_exists = TRUE;
        }
      }

      if (!$content_type_filter_exists) {
        $facet_new_parameters = array_merge($facet_parameters, ['content_type:prodotto']);
      }
    }
    else {
      $facet_new_parameters = ['content_type:prodotto'];
    }

    if (!empty($facet_new_parameters)) {
      $query->set('f', $facet_new_parameters);
    }
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.