You are here

public function AutocompleteController::autocomplete in Search API Autocomplete 8

Page callback: Retrieves autocomplete suggestions.

Parameters

\Drupal\search_api_autocomplete\SearchInterface $search_api_autocomplete_search: The search for which to retrieve autocomplete suggestions.

\Symfony\Component\HttpFoundation\Request $request: The request.

Return value

\Symfony\Component\HttpFoundation\JsonResponse The autocompletion response.

1 string reference to 'AutocompleteController::autocomplete'
search_api_autocomplete.routing.yml in ./search_api_autocomplete.routing.yml
search_api_autocomplete.routing.yml

File

src/Controller/AutocompleteController.php, line 80

Class

AutocompleteController
Provides a controller for autocompletion.

Namespace

Drupal\search_api_autocomplete\Controller

Code

public function autocomplete(SearchInterface $search_api_autocomplete_search, Request $request) {
  $matches = [];
  $search = $search_api_autocomplete_search;
  if (!$search
    ->status() || !$search
    ->hasValidIndex()) {
    return new JsonResponse($matches);
  }
  try {
    $keys = $request->query
      ->get('q');
    $split_keys = $this->autocompleteHelper
      ->splitKeys($keys);
    list($complete, $incomplete) = $split_keys;
    $data = $request->query
      ->all();
    unset($data['q']);
    $query = $search
      ->createQuery($complete, $data);
    if (!$query) {
      return new JsonResponse($matches);
    }

    // Prepare the query.
    $query
      ->setSearchId('search_api_autocomplete:' . $search
      ->id());
    $query
      ->addTag('search_api_autocomplete');

    // Get total limit and per-suggester limits.
    $limit = $search
      ->getOption('limit');
    $suggester_limits = $search
      ->getSuggesterLimits();

    // Get all enabled suggesters, ordered by weight.
    $suggesters = $search
      ->getSuggesters();
    $suggester_weights = $search
      ->getSuggesterWeights();
    $suggester_weights = array_intersect_key($suggester_weights, $suggesters);
    $suggester_weights += array_fill_keys(array_keys($suggesters), 0);
    asort($suggester_weights);

    /** @var \Drupal\search_api_autocomplete\Suggestion\SuggestionInterface[] $suggestions */
    $suggestions = [];

    // Go through all enabled suggesters in order of increasing weight and
    // add their suggestions (until the limit is reached).
    foreach (array_keys($suggester_weights) as $suggester_id) {

      // Clone the query in case the suggester makes any modifications.
      $tmp_query = clone $query;

      // Compute the applicable limit based on (remaining) total limit and
      // the suggester-specific limit (if set).
      if (isset($suggester_limits[$suggester_id])) {
        $suggester_limit = min($limit, $suggester_limits[$suggester_id]);
      }
      else {
        $suggester_limit = $limit;
      }
      $tmp_query
        ->range(0, $suggester_limit);

      // Add suggestions in a loop so we're sure they're numerically
      // indexed.
      $new_suggestions = $suggesters[$suggester_id]
        ->getAutocompleteSuggestions($tmp_query, $incomplete, $keys);
      foreach ($new_suggestions as $suggestion) {
        $suggestions[] = $suggestion;
        if (--$limit == 0) {

          // If we've reached the limit, stop adding suggestions.
          break 2;
        }
      }
    }

    // Allow other modules to alter the created suggestions.
    $alter_params = [
      'query' => $query,
      'search' => $search,
      'incomplete_key' => $incomplete,
      'user_input' => $keys,
    ];
    $this
      ->moduleHandler()
      ->alter('search_api_autocomplete_suggestions', $suggestions, $alter_params);

    // Then, add the suggestions to the $matches return array in the form
    // expected by Drupal's autocomplete Javascript.
    $show_count = $search
      ->getOption('show_count');
    foreach ($suggestions as $suggestion) {

      // If "Display result count estimates" was disabled, remove the
      // count from the suggestion.
      if (!$show_count) {
        $suggestion
          ->setResultsCount(NULL);
      }
      $build = $suggestion
        ->toRenderable();
      if ($build) {

        // Render the label.
        try {
          $label = $this->renderer
            ->render($build);
        } catch (\Exception $e) {
          watchdog_exception('search_api_autocomplete', $e, '%type while rendering an autocomplete suggestion: @message in %function (line %line of %file).');
          continue;
        }

        // Decide what the action of the suggestion is – entering specific
        // search terms or redirecting to a URL.
        if ($suggestion
          ->getUrl()) {

          // Generate an HTML-free version of the label to use as the value.
          // Setting the label as the value here is necessary for proper
          // accessibility via screen readers (which will otherwise read the
          // URL).
          $url = $suggestion
            ->getUrl()
            ->toString();
          $trimmed_label = trim(strip_tags((string) $label)) ?: $url;
          $matches[] = [
            'value' => $trimmed_label,
            'url' => $url,
            'label' => $label,
          ];
        }
        else {
          $matches[] = [
            'value' => trim($suggestion
              ->getSuggestedKeys()),
            'label' => $label,
          ];
        }
      }
    }
  } catch (SearchApiAutocompleteException $e) {
    watchdog_exception('search_api_autocomplete', $e, '%type while retrieving autocomplete suggestions: @message in %function (line %line of %file).');
  }
  return new JsonResponse($matches);
}