You are here

public function EntityHierarchy::getTree in Entity Reference Hierarchy 8.2

Same name and namespace in other branches
  1. 3.x modules/entity_hierarchy_workbench_access/src/Plugin/AccessControlHierarchy/EntityHierarchy.php \Drupal\entity_hierarchy_workbench_access\Plugin\AccessControlHierarchy\EntityHierarchy::getTree()

Gets the entire hierarchy tree.

This method will return a hierarcy tree from any supported source in a standard array structure. Using this method allows our code to abstract handling of access controls.

The array has the following components.

id - The lookup id of the entity or object (e.g. term tid). parents - A sorted array of ids for any parent items of this item. label - The human-readable label of the entity or object. description - A human-readable help description of this item. path - A fully-formed URL string for this item. depth - The depth in the hierarchy of this item. weight - The sort order (weight) of this item at its depth.

The first two items in this array (id, parents) are used to generate access control logic. The remaining items are used for building forms and user interfaces. Note that the last two items (depth, weight) are normally handled by the sorting done by the tree builder. They are provided in case your code needs to re-sort the tree.

Return value

array An array in the format defined above.

Overrides AccessControlHierarchyBase::getTree

File

modules/entity_hierarchy_workbench_access/src/Plugin/AccessControlHierarchy/EntityHierarchy.php, line 131

Class

EntityHierarchy
Defines a hierarchy based on an entity hierarchy field.

Namespace

Drupal\entity_hierarchy_workbench_access\Plugin\AccessControlHierarchy

Code

public function getTree() {
  if (!isset($this->tree)) {
    if ($data = $this->cacheBackend
      ->get(self::CACHE_ID)) {
      $this->tree = $data->data;
      return $this->tree;
    }
    $entity_type_id = $this->pluginDefinition['entity'];
    $entity_type = $this->entityTypeManager
      ->getDefinition($entity_type_id);
    $field_name = $this->pluginDefinition['field_name'];

    /** @var \PNX\NestedSet\Storage\DbalNestedSet $tree_storage */
    $tree_storage = $this->nestedSetStorageFactory
      ->get($field_name, $entity_type_id);
    $entityStorage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $query = $entityStorage
      ->getQuery();
    $tree = [];
    $or = $query
      ->orConditionGroup();
    $boolean_fields = $this->configuration['boolean_fields'];
    foreach ($boolean_fields as $boolean_field) {
      $or
        ->condition($boolean_field, 1);
      $tree[$boolean_field] = [];
    }
    if ($boolean_fields) {
      $query
        ->condition($or);
    }
    $valid_ids = $query
      ->execute();
    if (!$valid_ids) {
      return $tree;
    }
    $aggregate = $entityStorage
      ->getAggregateQuery();
    $aggregate
      ->groupBy($entity_type
      ->getKey('label'))
      ->groupBy($entity_type
      ->getKey('id'));
    foreach ($boolean_fields as $boolean_field) {
      $aggregate
        ->groupBy($boolean_field);
    }
    $aggregate
      ->condition($entity_type
      ->getKey('id'), $valid_ids, 'IN');
    $details = $aggregate
      ->execute();
    $boolean_parents = array_combine(array_column($details, $entity_type
      ->getKey('id')), array_map(function ($item) use ($entity_type) {

      // Remove the label/id fields.
      unset($item[$entity_type
        ->getKey('label')], $item[$entity_type
        ->getKey('id')]);

      // Return just the boolean fields that were checked.
      return array_keys(array_filter($item));
    }, $details));
    $entities = $entityStorage
      ->loadMultiple($valid_ids);
    $tags = $entityStorage
      ->getEntityType()
      ->getListCacheTags();
    foreach ($entities as $id => $entity) {

      /** @var \PNX\NestedSet\Node $node */
      $key = $this->keyFactory
        ->fromEntity($entity);
      $node = $tree_storage
        ->getNode($key);
      if (!$node) {
        continue;
      }
      $tags = array_merge($tags, $entities[$id]
        ->getCacheTags());
      foreach ($boolean_parents[$id] as $parent) {
        $cid = sprintf('%s:%s', self::CACHE_ID, $id);
        if ($entry = $this->cacheBackend
          ->get($cid)) {
          $tree[$parent][$id] = $entry->data;

          // Cache hit.
          continue;
        }
        $entry_tags = [];
        $entry = [
          'label' => $this
            ->generateEntityLabelWithAncestry($entity, $tree_storage, $entity_type_id, $entry_tags),
          'depth' => $node
            ->getDepth(),
          'parents' => [],
          'weight' => $node
            ->getLeft(),
          'description' => '',
        ];
        $tree[$parent][$id] = $entry;
        $this->cacheBackend
          ->set($cid, $entry, Cache::PERMANENT, $entry_tags);
        $tags = array_merge($tags, $entry_tags);
      }
    }
    foreach (array_keys($tree) as $parent) {
      uasort($tree[$parent], function (array $a, array $b) {

        // @todo Replace this with null coalesce and spaceship operator when
        // we only support PHP 7.0 or greater.
        $a_weight = 0;
        $b_weight = 0;
        if (isset($a['weight'])) {
          $a_weight = $a['weight'];
        }
        if (isset($b['weight'])) {
          $b_weight = $b['weight'];
        }
        if ($a_weight < $b_weight) {
          return -1;
        }
        if ($a_weight > $b_weight) {
          return 1;
        }
        return 0;
      });
    }
    $this->tree = $tree;
    $this->cacheBackend
      ->set(self::CACHE_ID, $tree, Cache::PERMANENT, array_unique($tags));
  }
  return $this->tree;
}