<?php
namespace Drupal\custom_module\Plugin\facets\hierarchy;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Condition\Annotation\Condition;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\facets\Hierarchy\HierarchyPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the Content hierarchy class.
*
* @FacetsHierarchy(
* id = "content",
* label = @Translation("Content hierarchy"),
* description = @Translation("Hierarchy structure provided by the Content module.")
* )
*/
class Content extends HierarchyPluginBase {
/**
* Static cache for the nested children.
*
* @var array
*/
protected $nestedChildren = [];
/**
* Static cache for the article parents.
*
* @var array
*/
protected $contentParents = [];
/**
* The article storage.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $contentStorage;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a Drupal\Component\Plugin\PluginBase 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.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
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')
);
}
/**
* Returns the content storage.
*
* @return \Drupal\core\Entity\EntityInterface
* The content storage.
*/
public function getContentStorage() {
if (!isset($this->contentStorage)) {
$this->contentStorage = $this->entityTypeManager->getStorage('node');
}
return $this->contentStorage;
}
/**
* {@inheritdoc}
*/
public function getParentIds($id) {
$current_nid = $id;
if ($current_nid != NULL) {
while ($parent = $this->contentGetParent($current_nid)) {
$current_nid = $parent;
$parents[$id][] = $parent;
}
return $parents[$id] ?? [];
}
}
/**
* {@inheritdoc}
*/
public function getNestedChildIds($id) {
if (isset($this->nestedChildren[$id])) {
return $this->nestedChildren[$id];
}
$parent = $this->getContentStorage()->load($id);
$storage = $this->getContentStorage();
$children = \Drupal::entityQuery('node')
->accessCheck(TRUE)
->condition('type', 'article')
->condition('field_parent_article', $parent->id())
->execute();
$children = $storage->loadMultiple($children);
$children = array_filter(array_values(array_map(function ($it) {
return $it->id();
}, $children)));
$subchilds = [];
foreach ($children as $child) {
$subchilds = array_merge($subchilds, $this->getNestedChildIds($child));
}
return $this->nestedChildren[$id] = array_merge($children, $subchilds);
}
/**
* {@inheritdoc}
*/
public function getChildIds(array $ids) {
$parents = [];
foreach ($ids as $id) {
$parent = $this->getContentStorage()->load($id);
$storage = $this->getContentStorage();
$children = \Drupal::entityQuery('node')
->accessCheck(TRUE)
->condition('type', 'article')
->condition('field_parent_article', $parent->id())
->execute();
$children = $storage->loadMultiple($children);
$parents[$id] = array_filter(array_values(array_map(function ($it) {
return $it->id();
}, $children)));
}
$parents = array_filter($parents);
return $parents;
}
/**
* {@inheritdoc}
*/
public function getSiblingIds(array $ids, array $activeIds = [], bool $parentSiblings = TRUE) {
if (empty($ids)) {
return [];
}
$parentIds = [];
$topLevelTerms = [];
foreach ($ids as $key => $id) {
if (!$activeIds || in_array($id, $activeIds)) {
$currentParentIds = $this->getParentIds($id);
if (!$currentParentIds) {
if (!$topLevelTerms) {
/** @var \Drupal\core\Entity\EntityInterface $entity */
$entity = $this->getContentStorage()->load($id);
// Issue #3260603:
// Due to a bug in core
// https://www.drupal.org/project/drupal/issues/2723323
// it may happen that a content is still referenced in a
// field, even though the term has been deleted. Not checking the
// term is empty produces a fatal error. The same could happen if
// someone manipulates the query parameter.
if (!$entity instanceof EntityInterface) {
unset($ids[$key]);
continue;
}
$topLevelTerms = array_map(function ($entity) {
return $entity->id;
}, $this->getContentStorage()->loadByProperties(['type' => $entity->bundle()]));
}
} else {
$parentIds[] = $currentParentIds;
}
}
}
$parentIds = array_unique(array_merge([], ...$parentIds));
$childIds = array_merge([], ...$this->getChildIds($parentIds));
return array_diff(
array_merge(
$childIds,
$topLevelTerms,
(!$topLevelTerms && $parentSiblings) ? $this->getSiblingIds($ids, $parentIds) : []
),
$ids
);
}
/**
* Returns the parent article for a given article, or false if no parent exists.
*
* @param int $nid
* A content node id.
*
* @return int|false
* Returns FALSE if no parent is found, else parent nid.
*/
protected function contentGetParent($nid) {
if ($nid) {
if (isset($this->contentParents[$nid])) {
return $this->contentParents[$nid];
}
}
$child = $this->getContentStorage()->load($nid);
$parents = $child->get('field_parent_article')->value;
if (empty($parents)) {
return FALSE;
}
return $this->contentParents[$nid] = reset($parents)->id();
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return Cache::mergeTags(parent::getCacheTags(), ['article_list']);
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return Cache::PERMANENT;
}
}
References: https://www.drupal.org/docs/contributed-modules/facets/hierarchical-date-granularity-facet.
References from the contrib module Facets:
- web/modules/contrib/facets/src/Plugin/facets/hierarchy/DateItems.php
- web/modules/contrib/facets/src/Plugin/facets/hierarchy/Taxonomy.php
Pre-requisites: Drupal Framework Knowledge
Audience: Drupal Developers