FacetBlock.php 5.13 KB
Newer Older
1 2
<?php

3
namespace Drupal\facets\Plugin\Block;
4 5

use Drupal\Core\Block\BlockBase;
6
use Drupal\Core\Entity\EntityStorageInterface;
7
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
8
use Drupal\Core\Plugin\PluginBase;
borisson_'s avatar
borisson_ committed
9
use Drupal\facets\FacetManager\DefaultFacetManager;
10 11 12
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
13
 * Exposes a facet rendered as a block.
14 15
 *
 * @Block(
16
 *   id = "facet_block",
borisson_'s avatar
borisson_ committed
17
 *   deriver = "Drupal\facets\Plugin\Block\FacetBlockDeriver"
18 19 20 21 22
 * )
 */
class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
23
   * The facet manager.
24
   *
25
   * @var \Drupal\facets\FacetManager\DefaultFacetManager
26
   */
27
  protected $facetManager;
28 29

  /**
30 31
   * The entity storage used for facets.
   *
32
   * @var \Drupal\Core\Entity\EntityStorageInterface
33 34 35 36 37
   */
  protected $facetStorage;

  /**
   * Construct a FacetBlock instance.
38 39 40 41 42 43 44
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param string $plugin_definition
   *   The plugin implementation definition.
borisson_'s avatar
borisson_ committed
45
   * @param \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager
46
   *   The facet manager.
47
   * @param \Drupal\Core\Entity\EntityStorageInterface $facet_storage
48
   *   The entity storage used for facets.
49
   */
50
  public function __construct(array $configuration, $plugin_id, $plugin_definition, DefaultFacetManager $facet_manager, EntityStorageInterface $facet_storage) {
borisson_'s avatar
borisson_ committed
51
    $this->facetManager = $facet_manager;
52
    $this->facetStorage = $facet_storage;
53 54 55 56 57 58 59 60 61 62 63
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
64 65
      $container->get('facets.manager'),
      $container->get('entity_type.manager')->getStorage('facets_facet')
66 67 68 69 70 71 72
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
73 74 75 76 77 78
    // The id saved in the configuration is in the format of
    // base_plugin:facet_id. We're splitting that to get to the facet id.
    $facet_mapping = $this->configuration['id'];
    $facet_id = explode(PluginBase::DERIVATIVE_SEPARATOR, $facet_mapping)[1];

    /** @var \Drupal\facets\FacetInterface $facet */
79
    $facet = $this->facetStorage->load($facet_id);
80

81 82 83 84 85
    // No need to build the facet if it does not need to be visible.
    if($facet->getOnlyVisibleWhenFacetSourceIsVisible() && !$facet->getFacetSource()->isRenderedInCurrentRequest()){
      return;
    }

86
    // Let the facet_manager build the facets.
87
    $build = $this->facetManager->build($facet);
88 89 90

    // Add contextual links only when we have results.
    if (!empty($build)) {
91 92 93
      $build['#contextual_links']['facets_facet'] = [
        'route_parameters' => ['facets_facet' => $facet->id()],
      ];
94
    }
95 96 97 98 99 100 101 102

    return $build;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    // A facet block cannot be cached, because it must always match the current
    // search results, and Search API gets those search results from a data
    // source that can be external to Drupal. Therefore it is impossible to
    // guarantee that the search results are in sync with the data managed by
    // Drupal. Consequently, it is not possible to cache the search results at
    // all. If the search results cannot be cached, then neither can the facets,
    // because they must always match.
    // Fortunately, facet blocks are rendered using a lazy builder (like all
    // blocks in Drupal), which means their rendering can be deferred (unlike
    // the search results, which are the main content of the page, and deferring
    // their rendering would mean sending an empty page to the user). This means
    // that facet blocks can be rendered and sent *after* the initial page was
    // loaded, by installing the BigPipe (big_pipe) module.
    //
    // When BigPipe is enabled, the search results will appear first, and then
    // each facet block will appear one-by-one, in DOM order.
    // See https://www.drupal.org/project/big_pipe.
    //
    // In a future version of Facet API, this could be refined, but due to the
    // reliance on external data sources, it will be very difficult if not
    // impossible to improve this significantly.
    //
    // Note: when using Drupal core's Search module instead of the contributed
126 127 128
    // Search API module, the above limitations do not apply, but for now it is
    // not considered worth the effort to optimize this just for Drupal core's
    // Search.
129 130 131
    return 0;
  }

132 133 134 135
  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
136 137
    // The ID saved in the configuration is of the format
    // 'base_plugin:facet_id'. We're splitting that to get to the facet ID.
138
    $facet_mapping = $this->getPluginId();
139
    $facet_id = explode(PluginBase::DERIVATIVE_SEPARATOR, $facet_mapping)[1];
140

141
    /** @var \Drupal\facets\FacetInterface $facet */
142
    $facet = $this->facetStorage->load($facet_id);
143

144
    return ['config' => [$facet->getConfigDependencyName()]];
145 146
  }

147
}