You are here

public function WorkspaceRepository::loadTree in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/workspaces/src/WorkspaceRepository.php \Drupal\workspaces\WorkspaceRepository::loadTree()

Returns an array of workspaces tree item properties, sorted in tree order.

Return value

array An array of workspace tree item properties, keyed by the workspace IDs. The tree item properties are:

  • depth: The depth of the workspace in the tree;
  • ancestors: The ancestor IDs of the workspace;
  • descendants: The descendant IDs of the workspace.

Overrides WorkspaceRepositoryInterface::loadTree

File

core/modules/workspaces/src/WorkspaceRepository.php, line 52

Class

WorkspaceRepository
Provides the default workspace tree lookup operations.

Namespace

Drupal\workspaces

Code

public function loadTree() {
  if (!isset($this->tree)) {
    $cache = $this->cache
      ->get('workspace_tree');
    if ($cache) {
      $this->tree = $cache->data;
      return $this->tree;
    }

    /** @var \Drupal\workspaces\WorkspaceInterface[] $workspaces */
    $workspaces = $this->entityTypeManager
      ->getStorage('workspace')
      ->loadMultiple();

    // First, sort everything alphabetically.
    uasort($workspaces, function (WorkspaceInterface $a, WorkspaceInterface $b) {
      return strnatcasecmp($a
        ->label(), $b
        ->label());
    });
    $tree_children = [];
    foreach ($workspaces as $workspace_id => $workspace) {
      $tree_children[$workspace->parent->target_id][] = $workspace_id;
    }

    // Keeps track of the parents we have to process, the last entry is used
    // for the next processing step. Top-level (root) workspace use NULL as
    // the parent, so we need to initialize the list with that value.
    $process_parents[] = NULL;

    // Loops over the parent entities and adds its children to the tree array.
    // Uses a loop instead of a recursion, because it's more efficient.
    $tree = [];
    while (count($process_parents)) {
      $parent = array_pop($process_parents);
      if (!empty($tree_children[$parent])) {
        $child_id = current($tree_children[$parent]);
        do {
          if (empty($child_id)) {
            break;
          }
          $tree[$child_id] = $workspaces[$child_id];
          if (!empty($tree_children[$child_id])) {

            // We have to continue with this parent later.
            $process_parents[] = $parent;

            // Use the current entity as parent for the next iteration.
            $process_parents[] = $child_id;

            // Move pointer so that we get the correct entity the next time.
            next($tree_children[$parent]);
            break;
          }
        } while ($child_id = next($tree_children[$parent]));
      }
    }

    // Generate a graph object in order to populate the `_depth`, `_ancestors`
    // and '_descendants' properties for all the entities.
    $graph = [];
    foreach ($workspaces as $workspace_id => $workspace) {
      $graph[$workspace_id]['edges'] = [];
      if (!$workspace->parent
        ->isEmpty()) {
        $graph[$workspace_id]['edges'][$workspace->parent->target_id] = TRUE;
      }
    }
    $graph = (new Graph($graph))
      ->searchAndSort();
    foreach (array_keys($tree) as $workspace_id) {
      $this->tree[$workspace_id] = [
        'depth' => count($graph[$workspace_id]['paths']),
        'ancestors' => array_keys($graph[$workspace_id]['paths']),
        'descendants' => isset($graph[$workspace_id]['reverse_paths']) ? array_keys($graph[$workspace_id]['reverse_paths']) : [],
      ];
    }

    // Use the 'workspace_list' entity type cache tag because it will be
    // invalidated automatically when a workspace is added, updated or
    // deleted.
    $this->cache
      ->set('workspace_tree', $this->tree, Cache::PERMANENT, $this->entityTypeManager
      ->getDefinition('workspace')
      ->getListCacheTags());
  }
  return $this->tree;
}