function LinksetController::toLinkTargetObjects

Same name and namespace in other branches
  1. 11.x core/modules/system/src/Controller/LinksetController.php \Drupal\system\Controller\LinksetController::toLinkTargetObjects()

Encode a menu tree as link items and capture any cacheability metadata.

This method recursively traverses the given menu tree to produce a flat array of link items encoded according the application/linkset+json media type.

To preserve hierarchical information, the target attribute contains a `hierarchy` member. Its value is an array containing the position of a link within a particular sub-tree prepended by the positions of its ancestors, and can be used to reconstruct a hierarchical data structure.

The reason that a `hierarchy` member is used instead of a `parent` or `children` member is because it is more compact, more suited to the linkset media type, and because it simplifies many menu operations. Specifically:

1. Creating a `parent` member would require each link to have an `id` in order to have something referenceable by the `parent` member. Reusing the link plugin IDs would not be viable because it would leak information about which modules are installed on the site. Therefore, this ID would have to be invented and would probably end up looking a lot like the `hierarchy` value. Finally, link IDs would encourage clients to hardcode the ID instead of using link relation types appropriately. 2. The linkset media type is not itself hierarchical. This means that `children` is infeasible without inventing our own Drupal-specific media type. 3. The `hierarchy` member can be used to efficiently perform tree operations that would otherwise be more complicated to implement. For example, by comparing the first X amount of hierarchy levels, you can find any subtree without writing recursive logic or complicated loops. Visit the URL below for more examples.

The structure of a `hierarchy` value is defined below.

A link which is a child of another link will always be prefixed by the exact value of their parent's hierarchy member. For example, if a link /bar is a child of a link /foo and /foo has a hierarchy member with the value ["1"], then the link /bar might have a hierarchy member with the value ["1", "0"]. The link /foo can be said to have depth 1, while the link /bar can be said to have depth 2.

Links which have the same parent (or no parent) have their relative order preserved in the final component of the hierarchy value.

According to the Linkset specification, each value in the hierarchy array must be a string. See https://tools.ietf.org/html/draft-ietf-httpapi-linkset-08#section-4.2.4…

Parameters

\Drupal\Core\Menu\MenuLinkTreeElement[] $tree: A tree of menu elements.

\Drupal\Core\Cache\RefinableCacheableDependencyInterface $cacheability: An object to capture any cacheability metadata.

array $hierarchy_ancestors: (Internal use only) The hierarchy value of the parent element if $tree is a subtree. Do not pass this value.

Return value

array An array which can be JSON-encoded to represent the given link tree.

See also

https://www.drupal.org/project/decoupled_menus/issues/3204132#comment-1…

1 call to LinksetController::toLinkTargetObjects()
LinksetController::process in core/modules/system/src/Controller/LinksetController.php
Serve linkset requests.

File

core/modules/system/src/Controller/LinksetController.php, line 148

Class

LinksetController
Linkset controller.

Namespace

Drupal\system\Controller

Code

protected function toLinkTargetObjects(array $tree, RefinableCacheableDependencyInterface $cacheability, $hierarchy_ancestors = []) : array {
    $links = [];
    // Calling array_values() discards any key names so that $index will be
    // numerical.
    foreach (array_values($tree) as $index => $element) {
        // Extract and preserve the access cacheability metadata.
        $element_access = $element->access;
        assert($element_access instanceof AccessResultInterface);
        $cacheability->addCacheableDependency($element_access);
        // If an element is not accessible, it should not be encoded. Its
        // cacheability should be preserved regardless, which is why that is done
        // outside of this conditional.
        if ($element_access->isAllowed()) {
            // Get and generate the URL of the link's target. This can create
            // cacheability metadata also.
            $url = $element->link
                ->getUrlObject();
            $generated_url = $url->toString(TRUE);
            $cacheability = $cacheability->addCacheableDependency($generated_url);
            // Take the hierarchy value for the current element and append it
            // to the link element parent's hierarchy value. See this method's
            // docblock for more context on why this value is the way it is.
            $hierarchy = $hierarchy_ancestors;
            array_push($hierarchy, strval($index));
            $link_options = $element->link
                ->getOptions();
            $link_attributes = $link_options['attributes'] ?? [];
            $link_rel = $link_attributes['rel'] ?? 'item';
            // Encode the link.
            $link = [
                'href' => $generated_url->getGeneratedUrl(),
                // @todo should this use the "title*" key if it is internationalized?
                // Follow up issue:
                // https://www.drupal.org/project/decoupled_menus/issues/3280735
'title' => $element->link
                    ->getTitle(),
                'hierarchy' => $hierarchy,
            ];
            $this->processCustomLinkAttributes($link, $link_attributes);
            $links[$link_rel][] = $link;
            // Recurse into the element's subtree.
            if (!empty($element->subtree)) {
                // Recursion!
                $links = array_merge_recursive($links, $this->toLinkTargetObjects($element->subtree, $cacheability, $hierarchy));
            }
        }
    }
    return $links;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.