You are here

class MenuTreeResource in REST Menu Tree 8

Same name and namespace in other branches
  1. 2.x src/Plugin/rest/resource/MenuTreeResource.php \Drupal\rest_menu_tree\Plugin\rest\resource\MenuTreeResource

Provides a resource to get view modes by entity and bundle.

Plugin annotation


@RestResource(
  id = "menu_tree",
  label = @Translation("Menu Tree"),
  serialization_class = "Drupal\system\Entity\Menu",
  uri_paths = {
    "canonical" = "/entity/menu/{menu}/tree"
  }
)

Hierarchy

Expanded class hierarchy of MenuTreeResource

File

src/Plugin/rest/resource/MenuTreeResource.php, line 32

Namespace

Drupal\rest_menu_tree\Plugin\rest\resource
View source
class MenuTreeResource extends ResourceBase {

  /**
   * Menu Tree.
   *
   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
   */
  protected $menuTree;

  /**
   * The entity manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, MenuLinkTreeInterface $menu_tree, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
    $this->menuTree = $menu_tree;
    $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
      ->getParameter('serializer.formats'), $container
      ->get('logger.factory')
      ->get('rest'), $container
      ->get('menu.link_tree'), $container
      ->get('entity_type.manager'));
  }

  /**
   * {@inheritdoc}
   */
  public function get(MenuInterface $menu) {
    $params = new MenuTreeParameters();
    $tree = $this->menuTree
      ->load($menu
      ->id(), $params);

    // Transform the tree using manipulators.
    $manipulators = [
      [
        'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
      ],
    ];
    $tree = $this->menuTree
      ->transform($tree, $manipulators);

    // Ensure that every item has an access response, if possible.
    $this
      ->addAccess($tree);

    // Maintain a clean version of the tree for adding cache metadata to the
    // response.
    $clean = $tree;

    // Remove items the user does not have access to.
    $this
      ->checkAccess($tree);

    // Remove the keys to prevent reordering.
    $this
      ->removeKeys($tree);
    $response = new ResourceResponse($tree);

    // First add every item to the cache.
    $response
      ->addCacheableDependency($menu);

    // Always use the clean version of the tree so every link is cached,
    // regardless of whether it's accessible to the current user or not.
    $this
      ->addCacheDependencies($clean, $response);
    return $response;
  }

  /**
   * Remove array keys.
   *
   * Javascript will re-sort the array based on the key. To prevent this, we'll
   * remove the array keys.
   */
  protected function removeKeys(array &$data) {
    $tree = [];
    foreach ($data as $value) {
      if ($value->subtree) {
        $this
          ->removeKeys($value->subtree);
      }
      $tree[] = $value;
    }
    $data = $tree;
  }

  /**
   * Add Access.
   *
   * Ensure that every item has an access result, if possible.
   */
  protected function addAccess(array $data) {
    foreach ($data as $value) {
      if ($value->access === NULL && $value->link instanceof AccessibleInterface) {
        $value->access = $value->link
          ->access('view', NULL, TRUE);
      }
      if ($value->subtree) {
        $this
          ->addAccess($value->subtree);
      }
    }
  }

  /**
   * Check Access.
   *
   * Remove items the user does not have access to from the response.
   */
  protected function checkAccess(array &$data) {
    foreach ($data as $key => $value) {

      // Use the menu links' access result.
      if ($value->access instanceof AccessResultInterface) {
        if (!$value->access
          ->isAllowed()) {
          unset($data[$key]);
          continue;
        }
      }
      elseif (!$value->link
        ->isEnabled()) {
        unset($data[$key]);
        continue;
      }
      if ($value->subtree) {
        $this
          ->checkAccess($value->subtree);
      }
    }
  }

  /**
   * Add Cache Tags.
   */
  protected function addCacheDependencies(array $data, CacheableResponseInterface $response) {
    foreach ($data as $value) {

      // Gather the access cacheability of every item in the menu link tree,
      // including inaccessible items. This allows us to render cache the menu
      // tree, yet still automatically vary the rendered menu by the same cache
      // contexts that the access results vary by.
      // However, if $value->access is not an AccessResultInterface object, this
      // will still render the menu link, because this method does not want to
      // require access checking to be able to render a menu tree.
      if ($value->access instanceof AccessResultInterface) {
        $response
          ->addCacheableDependency($value->access);
      }

      // Gather the cacheability of every item in the menu link tree. Some links
      // may be dynamic: they may have a dynamic text (e.g. a "Hi, <user>" link
      // text, which would vary by 'user' cache context), or a dynamic route
      // name or route parameters.
      $response
        ->addCacheableDependency($value->link);

      // There may be additional dependencies that must be manually determined.
      $this
        ->addLinkCacheDependencies($value->link, $response);
      if ($value->subtree) {
        $this
          ->addCacheDependencies($value->subtree, $response);
      }
    }
  }

  /**
   * Add Cache Tags.
   */
  protected function addLinkCacheDependencies(MenuLinkInterface $link, CacheableResponseInterface $response) {
    $entity_type = NULL;
    $entity_type_id = $link
      ->getBaseId();
    $uuid = $link
      ->getDerivativeId();
    if ($link instanceof EntityInterface) {
      $entity_type = $link
        ->getEntityType();
    }
    else {
      try {
        $entity_type = $this->entityTypeManager
          ->getDefinition($entity_type_id);
      } catch (\Exception $e) {

        // Silence is golden.
      }
    }
    if (!$entity_type) {
      return;
    }

    // Add the list cache tags.
    $cache = new CacheableMetadata();
    $cache
      ->addCacheTags($entity_type
      ->getListCacheTags());
    $response
      ->addCacheableDependency($cache);

    // If the link is an entity already, the cache tags were added in
    // ::addCacheDependencies().
    if ($link instanceof EntityInterface) {
      return;
    }

    // Get the entity.
    $entity = NULL;
    $storage = $this->entityTypeManager
      ->getStorage($entity_type_id);
    $metadata = $link
      ->getMetaData();
    if (!empty($metadata['entity_id'])) {
      $entity = $storage
        ->load($metadata['entity_id']);
    }
    else {
      $entities = $storage
        ->loadByProperties([
        $entity_type
          ->getKey('uuid') => $uuid,
      ]);
      $entity = reset($entities);
    }
    if (!$entity) {
      return;
    }
    $response
      ->addCacheableDependency($entity);
  }

  /**
   * {@inheritdoc}
   */
  protected function getBaseRoute($canonical_path, $method) {
    $route = parent::getBaseRoute($canonical_path, $method);
    $parameters = $route
      ->getOption('parameters') ?: array();
    $parameters['menu']['type'] = 'entity:menu';
    $route
      ->setOption('parameters', $parameters);
    return $route;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
MenuTreeResource::$entityTypeManager protected property The entity manager.
MenuTreeResource::$menuTree protected property Menu Tree.
MenuTreeResource::addAccess protected function Add Access.
MenuTreeResource::addCacheDependencies protected function Add Cache Tags.
MenuTreeResource::addLinkCacheDependencies protected function Add Cache Tags.
MenuTreeResource::checkAccess protected function Check Access.
MenuTreeResource::create public static function Creates an instance of the plugin. Overrides ResourceBase::create
MenuTreeResource::get public function
MenuTreeResource::getBaseRoute protected function Gets the base route for a particular method. Overrides ResourceBase::getBaseRoute
MenuTreeResource::removeKeys protected function Remove array keys.
MenuTreeResource::__construct public function Constructs a Drupal\rest\Plugin\ResourceBase object. Overrides ResourceBase::__construct
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
ResourceBase::$logger protected property A logger instance.
ResourceBase::$serializerFormats protected property The available serialization formats.
ResourceBase::availableMethods public function Returns the available HTTP request methods on this plugin. Overrides ResourceInterface::availableMethods 1
ResourceBase::getBaseRouteRequirements protected function Gets the base route requirements for a particular method. 1
ResourceBase::permissions public function Implements ResourceInterface::permissions(). Overrides ResourceInterface::permissions 2
ResourceBase::requestMethods protected function Provides predefined HTTP request methods.
ResourceBase::routes public function Returns a collection of routes with URL path information for the resource. Overrides ResourceInterface::routes
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.