Commit 57ba7bba authored by Markus Kalkbrenner's avatar Markus Kalkbrenner Committed by Markus Kalkbrenner
Browse files

Issue #3268623 by mkalkbrenner: Combine facets into one facet

parent adc574de
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -93,6 +93,20 @@ plugin.plugin_configuration.facets_processor.boolean_item:
      type: label
      label: 'Off value'

plugin.plugin_configuration.facets_processor.combine_processor:
  type: sequence
  label: 'Combine facets processor'
  sequence:
    type: mapping
    label: Mapping for a processor
    mapping:
      combine:
        type: boolean
        label: 'Combine this facet'
      mode:
        type: string
        label: 'Combination mode'

plugin.plugin_configuration.facets_processor.dependent_processor:
  type: sequence
  label: 'Dependent facet processor'
+11 −0
Original line number Diff line number Diff line
@@ -789,6 +789,17 @@ class Facet extends ConfigEntityBase implements FacetInterface {
    return $this->results;
  }

  /**
   * {@inheritdoc}
   */
  public function getResultsKeyedByRawValue() {
    $results = [];
    foreach ($this->results as $result) {
      $results[$result->getRawValue()] = $result;
    }
    return $results;
  }

  /**
   * {@inheritdoc}
   */
+8 −0
Original line number Diff line number Diff line
@@ -182,6 +182,14 @@ interface FacetInterface extends ConfigEntityInterface {
   */
  public function getResults();

  /**
   * Returns the result for the facet.
   *
   * @return \Drupal\facets\Result\ResultInterface[]
   *   The results of the facet.
   */
  public function getResultsKeyedByRawValue();

  /**
   * Sets the results for the facet.
   *
+170 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\facets\Plugin\facets\processor;

use Drupal\Core\Form\FormStateInterface;
use Drupal\facets\FacetInterface;
use Drupal\facets\Processor\BuildProcessorInterface;
use Drupal\facets\Processor\ProcessorPluginBase;
use Drupal\facets\FacetManager\DefaultFacetManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a processor that combines results of different facets.
 *
 * @FacetsProcessor(
 *   id = "combine_processor",
 *   label = @Translation("Combine facets"),
 *   description = @Translation("Combine the results of two or more facets. The raw value of a result item is used to identify a resuls item. It is up to you to ensure that the combination of the result sets makes sense. As the combination bases on the raw calues it makes sense to place this processor on an early position, especially before the URL handler"),
 *   stages = {
 *     "build" = 5
 *   }
 * )
 */
class CombineFacetProcessor extends ProcessorPluginBase implements BuildProcessorInterface, ContainerFactoryPluginInterface {

  /**
   * The language manager.
   *
   * @var \Drupal\facets\FacetManager\DefaultFacetManager
   */
  protected $facetsManager;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $facetStorage;

  /**
   * Constructs a new object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\facets\FacetManager\DefaultFacetManager $facets_manager
   *   The language manager.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, DefaultFacetManager $facets_manager, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);

    $this->facetsManager = $facets_manager;
    $this->facetStorage = $entity_type_manager->getStorage('facets_facet');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('facets.manager'),
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $current_facet) {
    $build = [];

    $config = $this->getConfiguration();

    // Loop over all defined blocks and filter them by provider, this builds an
    // array of blocks that are provided by the facets module.
    /** @var \Drupal\facets\Entity\Facet[] $facets */
    $facets = $this->facetStorage->loadMultiple();
    foreach ($facets as $facet) {
      if ($facet->id() === $current_facet->id()) {
        continue;
      }

      $build[$facet->id()]['label'] = [
        '#title' => $facet->getName() . ' (' . $facet->getFacetSourceId() . ')',
        '#type' => 'label',
      ];

      $build[$facet->id()]['combine'] = [
        '#title' => $this->t('Combine'),
        '#type' => 'checkbox',
        '#default_value' => !empty($config[$facet->id()]['combine']),
      ];

      $build[$facet->id()]['mode'] = [
        '#title' => $this->t('Mode'),
        '#type' => 'radios',
        '#options' => [
          'union' => $this->t("Add that facet's results to this facet's results (union)."),
          'diff' => $this->t("Only keep this facet's results that are not present in that facet's results (diff)."),
          'intersect' => $this->t('Only keep results that occur in both facets (intersect).'),
        ],
        '#default_value' => empty($config[$facet->id()]['mode']) ? NULL : $config[$facet->id()]['mode'],
        '#states' => [
          'visible' => [
            ':input[name="facet_settings[' . $this->getPluginId() . '][settings][' . $facet->id() . '][combine]"]' => ['checked' => TRUE],
          ],
        ],
      ];

    }

    return parent::buildConfigurationForm($form, $form_state, $current_facet) + $build;
  }

  /**
   * {@inheritdoc}
   */
  public function build(FacetInterface $facet, array $results) {
    $conditions = $this->getConfiguration();
    $enabled_combinations = [];

    foreach ($conditions as $facet_id => $condition) {
      if (empty($condition['combine'])) {
        continue;
      }
      $enabled_combinations[$facet_id] = $condition;
    }

    // Return as early as possible when there are no settings for allowed
    // facets.
    if (empty($enabled_combinations)) {
      return $results;
    }

    $results = $facet->getResultsKeyedByRawValue();

    foreach ($enabled_combinations as $facet_id => $settings) {
      /** @var \Drupal\facets\Entity\Facet $current_facet */
      $current_facet = $this->facetStorage->load($facet_id);
      $current_facet = $this->facetsManager->returnProcessedFacet($current_facet);

      switch ($settings['mode']) {
        case 'union':
          $results = $results + $current_facet->getResultsKeyedByRawValue();
          break;

        case 'diff':
          $results = array_diff_key($results, $current_facet->getResultsKeyedByRawValue());
          break;

        case 'intersect':
          $results = array_intersect_key($results, $current_facet->getResultsKeyedByRawValue());
          break;
      }
    }

    return $results;
  }

}