Skip to content
Snippets Groups Projects

Issue #3133309: Facet process to merge node types

Open Bram Driesen requested to merge issue/facets-3133309:3133309-merge_node_types into 3.0.x
Files
3
 
<?php
 
 
namespace Drupal\facets\Plugin\facets\processor;
 
 
use Drupal\Component\Utility\NestedArray;
 
use Drupal\Core\Entity\EntityTypeManagerInterface;
 
use Drupal\Core\Form\FormStateInterface;
 
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 
use Drupal\facets\FacetInterface;
 
use Drupal\facets\Processor\BuildProcessorInterface;
 
use Drupal\facets\Processor\ProcessorPluginBase;
 
use Drupal\facets\UrlProcessor\UrlProcessorPluginManager;
 
use Symfony\Component\DependencyInjection\ContainerInterface;
 
 
/**
 
* Class MergeNodeTypes allows merging content types into a single facet.
 
*
 
* @package Drupal\facets\Plugin\facets\processor
 
*
 
* @FacetsProcessor(
 
* id = "facets_merge_node_types",
 
* label = @Translation("Merge node types together."),
 
* description = @Translation("An integration to merge node types into a single facet result."),
 
* stages = {
 
* "build" = 80
 
* }
 
* )
 
*/
 
class MergeNodeTypesProcessor extends ProcessorPluginBase implements BuildProcessorInterface, ContainerFactoryPluginInterface {
 
 
/**
 
* The entity type manager.
 
*
 
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
 
*/
 
protected EntityTypeManagerInterface $entityTypeManager;
 
 
/**
 
* The URL processor manager.
 
*
 
* @var \Drupal\facets\UrlProcessor\UrlProcessorPluginManager
 
*/
 
protected UrlProcessorPluginManager $urlProcessorManager;
 
 
/**
 
* Constructs a DisplayValueWidgetOrderProcessor 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\Core\Entity\EntityTypeManagerInterface $entity_type_manager
 
* The entity type manager.
 
* @param \Drupal\facets\UrlProcessor\UrlProcessorPluginManager $url_processor_manager
 
* The url processor manager.
 
*/
 
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, UrlProcessorPluginManager $url_processor_manager) {
 
parent::__construct($configuration, $plugin_id, $plugin_definition);
 
$this->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
 
* An array of node types.
 
*
 
* @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'];
 
// If we only have one item, check if it's empty.
 
if (count($config) === 1 && empty($config[0]['facet_id'])) {
 
$config = [];
 
}
 
 
// Prepare form widget.
 
$build['#tree'] = TRUE;
 
$build['container_open']['#markup'] = '<div id="facet-group-fieldset-wrapper">';
 
 
// Gather the number of groups present in the form.
 
$config_count = $form_state->get('groups') ?: count($config);
 
$form_state->set('groups', $config_count);
 
 
// Iterate over available groups.
 
for ($i = 0; $i < $config_count; $i++) {
 
// Build details wrapper for each group.
 
$build['facet_groups'][$i] = [
 
'#type' => 'details',
 
'#title' => $this->t('Facet group'),
 
'#open' => FALSE,
 
];
 
 
$build['facet_groups'][$i]['facet_id'] = [
 
'#type' => 'textfield',
 
'#required' => TRUE,
 
'#title' => $this->t('New Facet id'),
 
'#default_value' => $config[$i]['facet_id'] ?? NULL,
 
];
 
 
$build['facet_groups'][$i]['facet_name'] = [
 
'#type' => 'textfield',
 
'#required' => TRUE,
 
'#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',
 
'#required' => TRUE,
 
'#title' => $this->t('Content types to be grouped.'),
 
'#options' => $this->getNodeTypes(),
 
'#default_value' => $config[$i]['content_types'] ?? [],
 
];
 
}
 
 
// Close container element.
 
$build['container_close']['#markup'] = '</div>';
 
 
// 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 ($config_count > 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
 
* The form array.
 
* @param \Drupal\Core\Form\FormStateInterface $form_state
 
* The form state interface.
 
*/
 
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
 
* The form array.
 
* @param \Drupal\Core\Form\FormStateInterface $form_state
 
* The form state interface.
 
*/
 
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
 
* The form array.
 
* @param \Drupal\Core\Form\FormStateInterface $form_state
 
* The form state interface.
 
*
 
* @return array
 
* The facet groups.
 
*/
 
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'] = '<div id="facet-group-fieldset-wrapper">';
 
$facet_groups['#suffix'] = '</div>';
 
 
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_id' => '',
 
'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();
 
$weight = 0;
 
 
array_walk($facet_groups, function ($config) use (&$facets, $filter_key, $field_name, &$weight) {
 
/** @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];
 
 
/** @var \Drupal\Core\Url $url */
 
$url = $first->getUrl();
 
/** @var array $query */
 
$query = [];
 
$i = 0;
 
$query[$filter_key][$i] = $field_name . ':' . $first->getRawValue();
 
 
// Walk through all remaining filtered types.
 
foreach ($filtered as $item) {
 
$i = $i + 1;
 
// Setup dynamic filter.
 
$query[$filter_key][$i] = $field_name . ':' . $item;
 
// Update facet count value when the last facet was found.
 
$first->setCount($first->getCount() + $facets[$item]->getCount());
 
unset($facets[$item]);
 
}
 
 
$request_query = \Drupal::request()->query->all();
 
if (!empty($request_query['f'])) {
 
$diffs = array_diff($request_query['f'], $query['f']);
 
 
// If we can only have one active filter, our facet only needs to have
 
// its own values combined with any other facet we might have. If we can
 
// select multiple facet values, we need to merge everything.
 
if ($first->getFacet()->getShowOnlyOneResult()) {
 
foreach ($diffs as $key => $value) {
 
if (strpos($value, $field_name . ':') !== FALSE) {
 
unset($diffs[$key]);
 
}
 
}
 
}
 
 
// If our current facet is active, we need to remove our own values from
 
// the query array so we can unset it by removing the values from the
 
// query.
 
if ($first->isActive()) {
 
$query['f'] = [];
 
}
 
 
// Merge the resulting diff and query parameters.
 
$query['f'] = array_merge($query['f'], $diffs);
 
}
 
 
$url->setOption('query', array_merge($request_query, $query));
 
 
// Overwrite label and raw value (id) and set the weight for sorting.
 
$first->setDisplayValue($this->t($config['facet_name']));
 
$first->setRawValue($config['facet_id']);
 
$first->set('groupWeight', $weight);
 
// Increment weight for next facet.
 
$weight++;
 
});
 
 
// Return in the order of configuration. Other sort handlers can change this
 
// later on with a processor.
 
usort($facets, function ($a, $b) {
 
return strcmp($a->get('groupWeight') :? 0, $b->get('groupWeight') :? 0);
 
});
 
 
return array_values($facets);
 
}
 
 
}
Loading