You are here

public function DefaultFacetManager::build in Facets 8

Builds a facet and returns it as a renderable array.

This method delegates to the relevant plugins to render a facet, it calls out to a widget plugin to do the actual rendering when results are found. When no results are found it calls out to the correct empty result plugin to build a render array.

Before doing any rendering, the processors that implement the BuildProcessorInterface enabled on this facet will run.

Parameters

\Drupal\facets\FacetInterface $facet: The facet we should build.

Return value

array Facet render arrays.

Throws

\Drupal\facets\Exception\InvalidProcessorException Throws an exception when an invalid processor is linked to the facet.

File

src/FacetManager/DefaultFacetManager.php, line 249

Class

DefaultFacetManager
The facet manager.

Namespace

Drupal\facets\FacetManager

Code

public function build(FacetInterface $facet) {

  // Immediately initialize the facets if they are not initiated yet.
  $this
    ->initFacets();

  // It might be that the facet received here, is not the same as the already
  // loaded facets in the FacetManager.
  // For that reason, get the facet from the already loaded facets in the
  // static cache.
  $facet = $this->facets[$facet
    ->id()];
  if ($facet
    ->getOnlyVisibleWhenFacetSourceIsVisible()) {

    // Block rendering and processing should be stopped when the facet source
    // is not available on the page. Returning an empty array here is enough
    // to halt all further processing.
    $facet_source = $facet
      ->getFacetSource();
    if (is_null($facet_source) || !$facet_source
      ->isRenderedInCurrentRequest()) {
      return [];
    }
  }

  // For clarity, process facets is called each build.
  // The first facet therefor will trigger the processing. Note that
  // processing is done only once, so repeatedly calling this method will not
  // trigger the processing more than once.
  $this
    ->processFacets($facet
    ->getFacetSourceId());

  // Get the current results from the facets and let all processors that
  // trigger on the build step do their build processing.
  // @see \Drupal\facets\Processor\BuildProcessorInterface.
  // @see \Drupal\facets\Processor\SortProcessorInterface.
  $results = $facet
    ->getResults();
  foreach ($facet
    ->getProcessorsByStage(ProcessorInterface::STAGE_BUILD) as $processor) {
    if (!$processor instanceof BuildProcessorInterface) {
      throw new InvalidProcessorException("The processor {$processor->getPluginDefinition()['id']} has a build definition but doesn't implement the required BuildProcessorInterface interface");
    }
    $results = $processor
      ->build($facet, $results);
  }

  // Handle hierarchy.
  if ($results && $facet
    ->getUseHierarchy()) {
    $keyed_results = [];
    foreach ($results as $result) {
      $keyed_results[$result
        ->getRawValue()] = $result;
    }
    $parent_groups = $facet
      ->getHierarchyInstance()
      ->getChildIds(array_keys($keyed_results));
    $keyed_results = $this
      ->buildHierarchicalTree($keyed_results, $parent_groups);

    // Remove children from primary level.
    foreach (array_unique($this->childIds) as $child_id) {
      unset($keyed_results[$child_id]);
    }
    $results = array_values($keyed_results);
  }

  // Trigger sort stage.
  $active_sort_processors = [];
  foreach ($facet
    ->getProcessorsByStage(ProcessorInterface::STAGE_SORT) as $processor) {
    $active_sort_processors[] = $processor;
  }

  // Sort the actual results if we have enabled sort processors.
  if (!empty($active_sort_processors)) {
    $results = $this
      ->sortFacetResults($active_sort_processors, $results);
  }
  $facet
    ->setResults($results);

  // We include this build even if empty, it may contain attached libraries.

  /** @var \Drupal\facets\Widget\WidgetPluginInterface $widget */
  $widget = $facet
    ->getWidgetInstance();
  $build = $widget
    ->build($facet);

  // No results behavior handling. Return a custom text or false depending on
  // settings.
  if (empty($facet
    ->getResults())) {
    $empty_behavior = $facet
      ->getEmptyBehavior();
    if ($empty_behavior && $empty_behavior['behavior'] === 'text') {
      return [
        [
          $build,
          '#type' => 'container',
          '#attributes' => [
            'data-drupal-facet-id' => $facet
              ->id(),
            'class' => [
              'facet-empty',
            ],
          ],
          'empty_text' => [
            // @codingStandardsIgnoreStart
            '#markup' => $this
              ->t($empty_behavior['text']),
          ],
        ],
      ];
    }
    else {

      // If the facet has no results, but it is being rendered trough ajax we
      // should render a container (that is empty). This is because the
      // javascript needs to be able to find a div to replace with the new
      // content.
      return [
        [
          $build,
          '#type' => 'container',
          '#attributes' => [
            'data-drupal-facet-id' => $facet
              ->id(),
            'class' => [
              'facet-empty',
              'facet-hidden',
            ],
          ],
        ],
      ];
    }
  }
  return [
    $build,
  ];
}