Holen Sie sich Menü Link Geschwister

11

Ich versuche, in Drupal 8 ein Menü zu erstellen, das nur Geschwisterlinks der aktuellen Seite enthält. Also, wenn das Menü ist:

  • Zuhause
  • Elternteil 1
    • Untereltern 1
      • Kind 1
    • Untereltern 2
      • Kind 2
      • Kind 3
      • Kind 4
  • Elternteil 2

Wenn ich mich auf der Seite "Kind 3" befinde, soll ein Menüblock wie folgt verlinkt werden:

  • Kind 2
  • Kind 3
  • Kind 4

Ich weiß, wie man das in D7 macht, denke ich, aber es fällt mir schwer, dieses Wissen in D8 zu übersetzen. Ist das überhaupt in D8 machbar? Und wenn ja, kann mich jemand in die richtige Richtung weisen, wie es geht?

Vielen Dank!

Erläuterung: Die untergeordnete Ebene muss variabel sein, damit Menüelemente mit unterschiedlichen Tiefen ihre Geschwister anzeigen können. So würde ich zum Beispiel nicht nur ein Menü für die Kinder wünschen, sondern auch ein Menü für die Untereltern und ein Menü für die Eltern und so weiter. Ich habe auch keine Kontrolle darüber, wie tief das Menü geht und ob es in allen Abschnitten so tief geht.

Erin McLaughlin
quelle

Antworten:

19

Am Ende fand ich einen Code heraus, mit dem ich dies tun konnte, indem ich einen benutzerdefinierten Block erstellte und in der Erstellungsmethode das Menü mit hinzugefügten Transformatoren ausgab. Dies ist der Link, über den ich herausgefunden habe, wie ich das Menü in den Block bekomme und Transformatoren hinzufüge: http://alexrayu.com/blog/drupal-8-display-submenu-block . Am build()Ende sah ich so aus:

$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'),
  ),
);

Der Transformator ist ein Dienst, daher habe ich intranet.services.ymlmeinem Intranet-Modul einen hinzugefügt , der auf die Klasse verweist, die ich letztendlich definiert habe. Die Klasse hatte drei Methoden: Diese wurde removeInactiveTrail()aufgerufen getCurrentParent(), um das übergeordnete stripChildren()Element der Seite abzurufen, auf der sich der Benutzer gerade befand, und das Menü wurde nur auf die untergeordneten Elemente des aktuellen Menüelements und seiner Geschwister reduziert (dh alle Untermenüs wurden entfernt, die nicht vorhanden waren). t im aktiven Trail).

So sah das aus:

/**
 * 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;
}

Ist das der beste Weg, es zu tun? Wahrscheinlich nicht. Aber es bietet zumindest einen Ausgangspunkt für Menschen, die etwas Ähnliches tun müssen.

Erin McLaughlin
quelle
Dies ist ziemlich genau das, was die Fußzeile macht. +1 für die Verwendung des menu.tree-Dienstes.
mradcliffe
2
Könnten Sie bitte angeben, welcher Code in der Datei service.yml abgelegt werden soll? Wie zeige ich eine Klasse aus der Datei service.yml?
Siddiq
Wie schließe ich den übergeordneten Menülink aus?
Permana
3

Drupal 8 verfügt über eine im Kern integrierte Menüblockfunktion. Sie müssen lediglich einen neuen Menüblock in der Block-Benutzeroberfläche erstellen und diesen konfigurieren.

Das passiert durch:

  • Platzieren eines neuen Blocks und Auswählen des Menüs, für das Sie einen Block erstellen möchten.
  • In der Blockkonfiguration müssen Sie die "Anfangsmenüebene" als 3 auswählen.
  • Möglicherweise möchten Sie auch die "Maximale Anzahl der anzuzeigenden Menüebenen" auf 1 setzen, wenn Sie nur Menüelemente aus der dritten Ebene drucken möchten.
lauriii
quelle
Leider kann ich nicht sicher sein, auf welcher Ebene sich die Seite befinden wird, daher kann ich nicht einfach einen Menüblock dafür erstellen. Es besteht auch die Möglichkeit, dass es sich um variable Ebenen handeln muss, je nachdem, wie die Site-Struktur letztendlich aussieht.
Erin McLaughlin
menu_block für Drupal 8 enthält derzeit keine Funktionen zum Verfolgen des aktuellen Knotens. Patches werden hier überprüft. drupal.org/node/2756675
Christian
OK für statische Verwendung. Aber nicht für den dynamischen Gebrauch wie in "Platziere einen Block auf jeder Seite und zeige die Geschwister der aktuellen Seite, egal auf welcher Ebene du dich gerade befindest."
Leymannx
3

Das Setzen des Stamms auf dem aktuellen Link kann die folgenden Tricks ausführen:

$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
quelle
Nein, das zeigt leider nur Kinder. Aber keine Geschwister. OP will Geschwister.
Leymannx
3

Geschwister-Menüblock

Mit Hilfe von @Icubes antworten und MenuLinkTreeInterface::getCurrentRouteMenuTreeParameterswir können einfach den aktiven Menüpfad der aktuellen Route abrufen . Damit haben wir auch den übergeordneten Menüpunkt. Wenn Sie dies als Ausgangspunkt MenuTreeParameters::setRootfür die Erstellung eines neuen Baums festlegen, erhalten Sie das gewünschte Geschwistermenü.

// 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
quelle
Diese Lösung wirkte wie ein Zauber. :)
Pankaj Sachdeva