Uzyskaj rodzeństwo linku do menu

11

Próbuję utworzyć menu w Drupal 8, które jest tylko rodzeństwem linków do bieżącej strony. Więc jeśli menu to:

  • Dom
  • Rodzic 1
    • Podrzędny 1
      • Dziecko 1
    • Podrzędny 2
      • Dziecko 2
      • Dziecko 3
      • Dziecko 4
  • Rodzic 2

Gdy jestem na stronie „Dziecko 3”, chcę, aby blok menu łączył się w następujący sposób:

  • Dziecko 2
  • Dziecko 3
  • Dziecko 4

Myślę, że wiem, jak to zrobić w D7, ale trudno mi przetłumaczyć tę wiedzę na D8. Czy to jest coś, co można zrobić w D8? A jeśli tak, to czy ktoś może skierować mnie w dobrym kierunku, jak to zrobić?

Dzięki!

Wyjaśnienie: Poziom dziecka musi być zmienny, aby elementy menu o różnych głębokościach mogły wyświetlać swoje rodzeństwo. Na przykład oprócz menu dla dzieci potrzebowałbym menu dla rodziców niepełnoletnich i menu dla rodziców i tak dalej. Nie mam również kontroli nad / wiedzą, jak głęboko schodzi menu i czy idzie tak głęboko na wszystkich sekcjach.

Erin McLaughlin
źródło

Odpowiedzi:

19

Skończyło się więc na znalezieniu kodu, który mi na to pozwoli, tworząc niestandardowy blok i, w metodzie kompilacji, wypisując menu z dodanymi transformatorami. To jest link, którego użyłem, aby dowiedzieć się, jak uzyskać menu w bloku i dodać do niego transformatory: http://alexrayu.com/blog/drupal-8-display-submenu-block . Mój build()wyglądał tak:

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

Transformator jest usługą, więc dodałem intranet.services.ymldo mojego modułu intranetowego, wskazując na klasę, którą ostatecznie zdefiniowałem. Klasa miała trzy metody: removeInactiveTrail()wywołania, getCurrentParent()aby uzyskać element nadrzędny strony, na której użytkownik był aktualnie, i stripChildren()która usunęła menu tylko z elementów potomnych bieżącego elementu menu i jego rodzeństwa (tj .: usunęła wszystkie podmenu, które nie były t na aktywnym szlaku).

Tak to wyglądało:

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

Czy to najlepszy sposób, aby to zrobić? Prawdopodobnie nie. Ale przynajmniej zapewnia miejsce początkowe dla osób, które muszą zrobić coś podobnego.

Erin McLaughlin
źródło
To właśnie robi footermap. +1 za korzystanie z usługi menu. Drzewo.
mradcliffe
2
Czy możesz powiedzieć, jaki kod powinien zostać umieszczony w pliku service.yml? Jak wskazać klasę z pliku service.yml?
siddiq,
Jak wykluczyć link menu rodzica / rodziców?
Permana,
3

Drupal 8 ma wbudowaną funkcjonalność bloku menu. Jedyne, co musisz zrobić, to utworzyć nowy blok menu w interfejsie użytkownika bloku i go skonfigurować.

Dzieje się tak przez:

  • Umieszczenie nowego bloku, a następnie wybranie menu, dla którego chcesz utworzyć blok.
  • W konfiguracji bloku musisz wybrać „Poziom menu początkowego” na 3.
  • Możesz także ustawić opcję „Maksymalna liczba poziomów menu do wyświetlenia” na 1, w przypadku gdy chcesz drukować tylko pozycje menu z trzeciego poziomu.
Lauriii
źródło
Niestety nie jestem pewien, na jakim poziomie będzie strona, więc nie mogę po prostu utworzyć dla niej bloku menu. Istnieje również możliwość, że może to wymagać różnych poziomów, w zależności od struktury strony.
Erin McLaughlin,
menu_block dla Drupala 8 obecnie nie zawiera funkcji śledzenia bieżącego węzła, łaty w przeglądzie tutaj; drupal.org/node/2756675
Christian
OK do użytku statycznego. Ale nie do użytku dynamicznego, jak w „Umieść blok na każdej stronie i pokaż rodzeństwo bieżącej strony bez względu na poziom, na którym obecnie jesteś”.
leymannx
3

Ustawienie katalogu głównego na bieżącym łączu może wykonać sztuczki:

$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
źródło
Nie, niestety to pokazuje tylko dzieci. Ale nie rodzeństwo. OP chce rodzeństwo.
leymannx,
3

Blok menu dla rodzeństwa

Za pomocą odpowiedzi @Icubes MenuLinkTreeInterface::getCurrentRouteMenuTreeParametersmożemy po prostu uzyskać informacje o aktywnym menu ścieżki aktualnej trasy. Dzięki temu mamy również element menu nadrzędnego. Ustawienie tego jako punktu wyjścia MenuTreeParameters::setRootdo budowy nowego drzewa daje pożądane menu rodzeństwa.

// 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
źródło
To rozwiązanie działało jak urok. :)
Pankaj Sachdeva