diff --git a/src/Plugin/facets/processor/MergeNodeTypes.php b/src/Plugin/facets/processor/MergeNodeTypes.php new file mode 100644 index 0000000..1a16a09 --- /dev/null +++ b/src/Plugin/facets/processor/MergeNodeTypes.php @@ -0,0 +1,357 @@ +entityTypeManager = $entity_type_manager; + $this->urlProcessorManager = $url_processor_manager; + } + + /** + * Creates an instance of the plugin. + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('plugin.manager.facets.url_processor') + ); + } + + /** + * Extract all available node types, then map them as valid options. + * + * @return array + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + protected function getNodeTypes(): array { + /** @var array $nodeTypes */ + $nodeTypes = array_map(function ($nodeType) { + /** @var \Drupal\node\Entity\NodeType $nodeType */ + return $nodeType->label(); + }, $this->entityTypeManager->getStorage('node_type')->loadMultiple()); + + return $nodeTypes; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet) { + /** @var array $config */ + $config = $this->getConfiguration()['facet_groups']; + + // Gather the number of groups present in the form. + $groups = $form_state->get('groups'); + + // Ensure that there is at least one group. + if (is_null($groups)) { + $groups = count($config); + $form_state->set('groups', $groups); + } + + // Prepare form widget. + $build['#tree'] = TRUE; + $build['container_open']['#markup'] = '
'; + + // Iterate over available groups. + for ($i = 0; $i < $groups; $i++) { + + // Build details wrapper for each group. + $build['facet_groups'][$i] = [ + '#type' => 'details', + '#title' => $this->t('Facet group'), + '#open' => FALSE, + ]; + + // Include field to overwrite facet name. + $build['facet_groups'][$i]['facet_name'] = [ + '#type' => 'textfield', + '#title' => $this->t('New Facet name'), + '#default_value' => $config[$i]['facet_name'] ?? NULL, + ]; + + // Expose all available content types. + $build['facet_groups'][$i]['content_types'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Content types to be grouped.'), + '#options' => $this->getNodeTypes(), + '#default_value' => $config[$i]['content_types'] ?? [], + ]; + } + + // Close container element. + $build['container_close']['#markup'] = '
'; + + // Setup $.ajax buttons. + $build['actions'] = [ + '#type' => 'actions', + ]; + $build['actions']['add_group'] = [ + '#type' => 'submit', + '#value' => $this->t('Add another group'), + '#submit' => [ + [$this, 'addOne'], + ], + '#ajax' => [ + 'callback' => [$this, 'addMoreCallback'], + 'wrapper' => 'facet-group-fieldset-wrapper', + ], + ]; + + // If there is more than one group, add the remove button. + if ($groups > 1) { + $build['actions']['remove_group'] = [ + '#type' => 'submit', + '#value' => $this->t('Remove last group'), + '#submit' => [ + [$this, 'removeOne'], + ], + '#ajax' => [ + 'callback' => [$this, 'addMoreCallback'], + 'wrapper' => 'facet-group-fieldset-wrapper', + ], + ]; + } + + return $build; + } + + /** + * Submit handler for the "Add another group" button. + * + * Increments the max counter and causes a rebuild. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public function addOne(array &$form, FormStateInterface $form_state) { + $groups = $form_state->get('groups'); + $add_button = $groups + 1; + $form_state->set('groups', $add_button); + + // Since our buildForm() method relies on the value of 'num_names' to + // generate 'name' form elements, we have to tell the form to rebuild. If we + // don't do this, the form builder will not call buildForm(). + $form_state->setRebuild(); + } + + /** + * Submit handler for the "Remove last group" button. + * + * Decrements the max counter and causes a form rebuild. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public function removeOne(array &$form, FormStateInterface $form_state) { + $groups = $form_state->get('groups'); + if ($groups > 1) { + $remove_button = $groups - 1; + $form_state->set('groups', $remove_button); + } + + // Since our buildForm() method relies on the value of 'num_names' to + // generate 'name' form elements, we have to tell the form to rebuild. If we + // don't do this, the form builder will not call buildForm(). + $form_state->setRebuild(); + } + + /** + * Callback for both ajax-enabled buttons. + * + * Selects and returns the fieldset with the names in it. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return array + */ + public function addMoreCallback(array &$form, FormStateInterface $form_state) { + /** @var array $facet_groups */ + $facet_groups = NestedArray::getValue($form, [ + 'facet_settings', + 'facets_merge_node_types', + 'settings', + 'facet_groups', + ]); + + // Recreate container wrapper. + $facet_groups['#prefix'] = '
'; + $facet_groups['#suffix'] = '
'; + + return $facet_groups; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet) { + $form_state->unsetValue('actions'); + parent::submitConfigurationForm($form, $form_state, $facet); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'facet_groups' => [ + [ + 'facet_name' => '', + 'content_types' => [], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function build(FacetInterface $facet, array $results) { + /** @var array $facet_groups */ + $facet_groups = $this->getConfiguration()['facet_groups']; + + /** @var \Drupal\facets\Result\Result[] $facets */ + $facets = array_reduce($results, function ($carry, $item) { + /** @var \Drupal\facets\Result\Result $item */ + $carry[$item->getRawValue()] = $item; + return $carry; + }, []); + + $url_processor = $this->urlProcessorManager->createInstance($facet->getFacetSourceConfig() + ->getUrlProcessorName(), ['facet' => $facet]); + $filter_key = $url_processor->getFilterKey(); + $field_name = $facet->getUrlAlias(); + + array_walk($facet_groups, function ($config) use ($results, &$facets, $filter_key, $field_name) { + /** @var array $types */ + $types = array_filter($config['content_types']); + if (empty($types)) { + return; + } + + /** @var array $filtered */ + $filtered = array_filter($types, function ($type) use ($facets) { + return array_key_exists($type, $facets); + }); + if (empty($filtered)) { + return; + } + + /** @var string $key */ + $key = array_shift($filtered); + /** @var \Drupal\facets\Result\Result $first */ + $first = &$facets[$key]; + + // Overwrite label if new facet name was defined. + if (!empty($config['facet_name'])) { + $facet_name = $this->t($config['facet_name']); + $first->setDisplayValue($facet_name); + } + + // Init flag variables. + $updated = FALSE; + + /** @var \Drupal\Core\Url $url */ + $url = $first->getUrl(); + /** @var array $query */ + $query = $url->getOption('query'); + + // Walk through all remaining filtered types. + foreach ($filtered as $item) { + // Setup dynamic filter. + $filter = "{$field_name}:{$item}"; + + // Look-up for query string. + if (empty($query[$filter_key]) || !in_array($filter, $query[$filter_key])) { + // Inject filter to current query. + $updated = TRUE; + $query[$filter_key][] = $filter; + } + // Verify that current facet is active. + elseif ($first->isActive()) { + // Remove duplication filter values. + $updated = TRUE; + $query[$filter_key] = array_filter($query[$filter_key], function ($param) use ($filter) { + return $param != $filter; + }); + + // Remove whole query string when there are not filters. + if (empty($query[$filter_key])) { + unset($query[$filter_key]); + } + } + + // Overwrite URL options then define it back to facet. + if ($updated) { + $url->setOption('query', $query); + $first->setUrl($url); + } + + // Update facet count value when lab facet was found. + $first->setCount($first->getCount() + $facets[$item]->getCount()); + + // Remove facet instance. + unset($facets[$item]); + } + }); + + return array_values($facets); + } + +}