Obtenir les frères et sœurs de lien de menu

11

J'essaie de créer un menu dans Drupal 8 qui n'est que des liens frères et sœurs de la page actuelle. Donc, si le menu est:

  • Accueil
  • Parent 1
    • Sous-parent 1
      • Enfant 1
    • Sous-parent 2
      • Enfant 2
      • Enfant 3
      • Enfant 4
  • Parent 2

Lorsque je suis sur la page "Enfant 3", je veux qu'un bloc de menu soit lié à ceci:

  • Enfant 2
  • Enfant 3
  • Enfant 4

Je sais comment faire cela en D7, je pense, mais j'ai du mal à traduire ces connaissances en D8. Est-ce même quelque chose qui est faisable en D8? Et si c'est le cas, quelqu'un peut-il m'orienter dans la bonne direction sur la façon de le faire?

Merci!

Clarification: le niveau enfant doit être variable, afin que les éléments de menu avec différentes profondeurs puissent afficher leurs frères et sœurs. Ainsi, par exemple, en plus de vouloir un menu pour les enfants, j'aurais besoin d'un menu pour les sous-parents et d'un menu pour les parents et ainsi de suite. Je n'ai également aucun contrôle / connaissance de la profondeur du menu et de sa profondeur dans toutes les sections.

Erin McLaughlin
la source

Réponses:

19

J'ai donc fini par trouver du code qui me permettrait de le faire, en créant un bloc personnalisé et, dans la méthode de construction, en affichant le menu avec des transformateurs ajoutés. C'est le lien que j'ai utilisé pour comprendre comment obtenir le menu dans le bloc et y ajouter des transformateurs: http://alexrayu.com/blog/drupal-8-display-submenu-block . J'ai build()fini par ressembler à ceci:

$menu_tree = \Drupal::menuTree();
$menu_name = 'main';

// Build the typical default set of menu tree parameters.
$parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);

// Load the tree based on this set of parameters.
$tree = $menu_tree->load($menu_name, $parameters);

// Transform the tree using the manipulators you want.
$manipulators = array(
  // Only show links that are accessible for the current user.
  array('callable' => 'menu.default_tree_manipulators:checkAccess'),
  // Use the default sorting of menu links.
  array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
  // Remove all links outside of siblings and active trail
  array('callable' => 'intranet.menu_transformers:removeInactiveTrail'),
);
$tree = $menu_tree->transform($tree, $manipulators);

// Finally, build a renderable array from the transformed tree.
$menu = $menu_tree->build($tree);

return array(
  '#markup' => \Drupal::service('renderer')->render($menu),
  '#cache' => array(
    'contexts' => array('url.path'),
  ),
);

Le transformateur est un service, j'ai donc ajouté un intranet.services.ymlà mon module intranet, pointant vers la classe que j'ai fini par définir. La classe avait trois méthodes:, removeInactiveTrail()qui appelait getCurrentParent()pour obtenir le parent de la page sur laquelle l'utilisateur était actuellement, et stripChildren(), qui supprimait le menu uniquement aux enfants de l'élément de menu actuel et de ses frères et sœurs (c'est-à-dire: supprimait tous les sous-menus qui étaient ' t dans la piste active).

Voici à quoi cela ressemblait:

/**
 * Removes all link trails that are not siblings to the active trail.
 *
 * For a menu such as:
 * Parent 1
 *  - Child 1
 *  -- Child 2
 *  -- Child 3
 *  -- Child 4
 *  - Child 5
 * Parent 2
 *  - Child 6
 * with current page being Child 3, Parent 2, Child 6, and Child 5 would be
 * removed.
 *
 * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
 *   The menu link tree to manipulate.
 *
 * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
 *   The manipulated menu link tree.
 */
public function removeInactiveTrail(array $tree) {
  // Get the current item's parent ID
  $current_item_parent = IntranetMenuTransformers::getCurrentParent($tree);

  // Tree becomes the current item parent's children if the current item
  // parent is not empty. Otherwise, it's already the "parent's" children
  // since they are all top level links.
  if (!empty($current_item_parent)) {
    $tree = $current_item_parent->subtree;
  }

  // Strip children from everything but the current item, and strip children
  // from the current item's children.
  $tree = IntranetMenuTransformers::stripChildren($tree);

  // Return the tree.
  return $tree;
}

/**
 * Get the parent of the current active menu link, or return NULL if the
 * current active menu link is a top-level link.
 *
 * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
 *   The tree to pull the parent link out of.
 * @param \Drupal\Core\Menu\MenuLinkTreeElement|null $prev_parent
 *   The previous parent's parent, or NULL if no previous parent exists.
 * @param \Drupal\Core\Menu\MenuLinkTreeElement|null $parent
 *   The parent of the current active link, or NULL if not parent exists.
 *
 * @return \Drupal\Core\Menu\MenuLinkTreeElement|null
 *   The parent of the current active menu link, or NULL if no parent exists.
 */
private function getCurrentParent($tree, $prev_parent = NULL, $parent = NULL) {
  // Get active item
  foreach ($tree as $leaf) {
    if ($leaf->inActiveTrail) {
      $active_item = $leaf;
      break;
    }
  }

  // If the active item is set and has children
  if (!empty($active_item) && !empty($active_item->subtree)) {
    // run getCurrentParent with the parent ID as the $active_item ID.
    return IntranetMenuTransformers::getCurrentParent($active_item->subtree, $parent, $active_item);
  }

  // If the active item is not set, we know there was no active item on this
  // level therefore the active item parent is the previous level's parent
  if (empty($active_item)) {
    return $prev_parent;
  }

  // Otherwise, the current active item has no children to check, so it is
  // the bottommost and its parent is the correct parent.
  return $parent;
}


/**
 * Remove the children from all MenuLinkTreeElements that aren't active. If
 * it is active, remove its children's children.
 *
 * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
 *   The menu links to strip children from non-active leafs.
 *
 * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
 *   A menu tree with no children of non-active leafs.
 */
private function stripChildren($tree) {
  // For each item in the tree, if the item isn't active, strip its children
  // and return the tree.
  foreach ($tree as &$leaf) {
    // Check if active and if has children
    if ($leaf->inActiveTrail && !empty($leaf->subtree)) {
      // Then recurse on the children.
      $leaf->subtree = IntranetMenuTransformers::stripChildren($leaf->subtree);
    }
    // Otherwise, if not the active menu
    elseif (!$leaf->inActiveTrail) {
      // Otherwise, it's not active, so we don't want to display any children
      // so strip them.
      $leaf->subtree = array();
    }
  }

  return $tree;
}

Est-ce la meilleure façon de le faire? Probablement pas. Mais cela fournit au moins un point de départ pour les personnes qui ont besoin de faire quelque chose de similaire.

Erin McLaughlin
la source
C'est à peu près ce que fait la footermap. +1 pour l'utilisation du service menu.tree.
mradcliffe
2
Pourriez-vous indiquer le code à placer dans le fichier service.yml? Comment pointer une classe du fichier service.yml?
siddiq
Comment exclure le lien du menu parent / s?
Permana
3

Drupal 8 a une fonctionnalité de bloc de menu intégrée au cœur de la seule chose que vous avez à faire est de créer un nouveau bloc de menu dans l'interface utilisateur du bloc et de le configurer.

Cela se produit par:

  • Placer un nouveau bloc, puis sélectionner le menu pour lequel vous souhaitez créer un bloc.
  • Dans la configuration de bloc, vous devez sélectionner le "Niveau de menu initial" à 3.
  • Vous pouvez également définir le "Nombre maximal de niveaux de menu à afficher" sur 1 au cas où vous ne souhaitez imprimer que les éléments de menu du troisième niveau.
lauriii
la source
Malheureusement, je ne peux pas être sûr du niveau auquel la page va se trouver, donc je ne peux pas simplement créer un bloc de menu pour cela. Il y a aussi la possibilité qu'il doive être de niveaux variables, selon ce que la structure du site finit par être.
Erin McLaughlin
menu_block pour Drupal 8 n'inclut pas actuellement de fonctionnalité pour suivre le nœud actuel, correctifs en revue ici; drupal.org/node/2756675
Christian
OK pour une utilisation statique. Mais pas pour une utilisation dynamique comme dans "Placer un bloc sur chaque page et afficher les frères et sœurs de la page actuelle, quel que soit le niveau dans lequel vous vous trouvez actuellement."
leymannx
3

La définition de la racine sur le lien actuel peut faire les tours:

$menu_tree = \Drupal::menuTree();
$menu_name = 'main';

$parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);
$currentLinkId = reset($parameters->activeTrail);
$parameters->setRoot($currentLinkId);
$tree = $menu_tree->load($menu_name, $parameters);

// Transform the tree using the manipulators you want.
$manipulators = array(
  // Only show links that are accessible for the current user.
  array('callable' => 'menu.default_tree_manipulators:checkAccess'),
  // Use the default sorting of menu links.
  array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $menu_tree->transform($tree, $manipulators);
lcube
la source
Non, malheureusement, cela ne montre que les enfants. Mais pas les frères et sœurs. OP veut des frères et sœurs.
leymannx
3

Bloc de menu de frères et sœurs

À l'aide de la réponse @Icubes, MenuLinkTreeInterface::getCurrentRouteMenuTreeParametersnous pouvons simplement obtenir la trace de menu active de l'itinéraire actuel. Ayant cela, nous avons également l'élément de menu parent. Définir cela comme point de départ via MenuTreeParameters::setRootpour construire un nouvel arbre vous donne le menu des frères et sœurs souhaité.

// Enable url-wise caching.
$build = [
  '#cache' => [
    'contexts' => ['url'],
  ],
];

$menu_name = 'main';
$menu_tree = \Drupal::menuTree();

// This one will give us the active trail in *reverse order*.
// Our current active link always will be the first array element.
$parameters   = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);
$active_trail = array_keys($parameters->activeTrail);

// But actually we need its parent.
// Except for <front>. Which has no parent.
$parent_link_id = isset($active_trail[1]) ? $active_trail[1] : $active_trail[0];

// Having the parent now we set it as starting point to build our custom
// tree.
$parameters->setRoot($parent_link_id);
$parameters->setMaxDepth(1);
$parameters->excludeRoot();
$tree = $menu_tree->load($menu_name, $parameters);

// Optional: Native sort and access checks.
$manipulators = [
  ['callable' => 'menu.default_tree_manipulators:checkNodeAccess'],
  ['callable' => 'menu.default_tree_manipulators:checkAccess'],
  ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
];
$tree = $menu_tree->transform($tree, $manipulators);

// Finally, build a renderable array.
$menu = $menu_tree->build($tree);

$build['#markup'] = \Drupal::service('renderer')->render($menu);

return $build;
leymannx
la source
Cette solution a fonctionné comme un charme. :)
Pankaj Sachdeva