Ottieni fratelli di link al menu


11

Sto cercando di creare un menu in Drupal 8 che è solo un collegamento tra fratelli della pagina corrente. Quindi se il menu è:

  • Casa
  • Genitore 1
    • Sotto-genitore 1
      • Bambino 1
    • Sotto-genitore 2
      • Bambino 2
      • Bambino 3
      • Bambino 4
  • Genitore 2

Quando sono sulla pagina "Child 3", voglio che un blocco di menu da collegare sia simile al seguente:

  • Bambino 2
  • Bambino 3
  • Bambino 4

So come farlo in D7, penso, ma sto facendo fatica a tradurre questa conoscenza in D8. È forse qualcosa di fattibile in D8? E se lo è, qualcuno può indicarmi la giusta direzione su come farlo?

Grazie!

Chiarimento: il livello figlio deve essere variabile, in modo che le voci di menu con profondità diverse possano visualizzare i loro fratelli. Quindi, per esempio, oltre a desiderare un menu per i bambini, avrei bisogno di un menu per i sotto-genitori e un menu per i genitori e così via. Inoltre, non ho alcun controllo / conoscenza della profondità del menu e della profondità di tutte le sezioni.

Risposte:


19

Così, ho finito per capire un po 'di codice che mi avrebbe permesso di farlo, creando un blocco personalizzato e, nel metodo di generazione, producendo il menu con i trasformatori aggiunti ad esso. Questo è il link che ho usato per capire come ottenere il menu nel blocco e aggiungere trasformatori: http://alexrayu.com/blog/drupal-8-display-submenu-block . Ho build()finito per apparire così:

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

Il trasformatore è un servizio, quindi l'ho aggiunto intranet.services.ymlal mio modulo Intranet, indicando la classe che ho finito per definire. La classe aveva tre metodi:, removeInactiveTrail()che chiamava getCurrentParent()per ottenere il genitore della pagina in cui si trovava l'utente attualmente, e stripChildren(), che riduceva il menu solo ai figli della voce di menu corrente e ai suoi fratelli (cioè: rimossi tutti i sottomenu che erano ' t nella traccia attiva).

Questo è quello che sembrava:

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

È questo il modo migliore per farlo? Probabilmente no. Ma almeno fornisce un punto di partenza per le persone che hanno bisogno di fare qualcosa di simile.


Questo è praticamente ciò che fa footermap. +1 per l'utilizzo del servizio menu.tree.
mradcliffe,

2
Potresti dire quale codice deve essere inserito nel file service.yml? Come puntare una classe dal file service.yml?
siddiq,

Come escludere il collegamento al menu genitore / i?
Permana,

3

Drupal 8 ha la funzionalità Blocco menu integrata nel core, l'unica cosa che devi fare è creare un nuovo blocco menu nell'interfaccia utente del blocco e configurarlo.

Ciò accade per:

  • Posizionare un nuovo blocco e quindi selezionare il menu per cui si desidera creare un blocco.
  • Nella configurazione del blocco devi selezionare "Livello menu iniziale" su 3.
  • È inoltre possibile impostare il "Numero massimo di livelli di menu da visualizzare" su 1 nel caso in cui si desideri stampare solo le voci di menu dal terzo livello.

Sfortunatamente, non posso essere sicuro del livello della pagina, quindi non posso semplicemente creare un blocco di menu per esso. C'è anche la possibilità che possa essere necessario che siano livelli variabili, a seconda di ciò che la struttura del sito finisce per essere.
Erin McLaughlin,

menu_block per Drupal 8 al momento non include funzionalità per seguire il nodo corrente, patch qui esaminate; drupal.org/node/2756675
Christian

OK per uso statico. Ma non per un uso dinamico come in "Posiziona un blocco su ogni pagina e mostra i fratelli della pagina corrente, indipendentemente dal livello in cui ti trovi attualmente."
leymannx,

3

L'impostazione della radice sul collegamento corrente potrebbe fare i trucchi:

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

No, purtroppo questo mostra solo i bambini. Ma non fratelli. OP vuole fratelli.
leymannx,

3

Blocco menu fratelli

Con l'aiuto della risposta @Icubes MenuLinkTreeInterface::getCurrentRouteMenuTreeParameterspossiamo semplicemente ottenere la traccia del menu attivo del percorso corrente. Detto ciò, abbiamo anche la voce di menu principale. Impostandolo come punto di partenza tramite MenuTreeParameters::setRootper costruire un nuovo albero, si ottiene il menu fratelli desiderato.

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

Questa soluzione ha funzionato come un fascino. :)
Pankaj Sachdeva,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.