it-swarm.com.de

Wie erhalte ich einen Abschnitt des Menübaums basierend auf dem aktuellen Knoten?

Ich habe derzeit Probleme, mich mit der Funktionsweise des Menübaums und der Menüverknüpfungsdienste für die Ausgabe eines Teils eines Menüs mit einigen benutzerdefinierten Anforderungen zu befassen. Zugegeben, ich muss das nicht sehr oft tun, aber es scheint, dass es einen Weg geben muss.

Ich habe es mit Menu Block versucht, aber das scheint nicht in der Lage zu sein, das zu tun, was ich tun muss.

Wenn ich mich auf Ebene 3 und einem bestimmten Inhaltstyp befinde (oder die übergeordnete Route nicht verknüpft ist), muss ich zunächst die aktuelle Ebene des Menüs wie folgt drucken:

(enter image description here

Die Menü-Links repräsentieren, was sich unter der aktuellen Seite befindet.

Zweitens, wenn es sich um Level 2+ handelt (und nicht ein bestimmter Inhaltstyp ist), muss der übergeordnete Abschnitt als blauer Link oben in sitzen eine andere Behandlung:

(enter image description here

Drittens, wenn die aktuelle Seite im Menü aktiv ist, werden keine Geschwister gedruckt, nur dieser Seitenmenü-Link und seine untergeordneten Elemente:

(enter image description here

Ich habe hier in einem Menüblock angefangen:

/** @var \Drupal\Core\Menu\MenuLinkManager $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$route_params = array('node' => $node->id());

$menu_links = $menu_link_manager->loadLinksByRoute('entity.node.canonical', $route_params, 'main');

// Attach the top parent, if we are at level 3 (for a project) or level 2 (for a basic page)
// top parent cannot go above level 3.... but also level 2?
// show the current node menu tree and its siblings
// show the siblings if the current page has no children

$parent = reset($menu_links)->getParent();
$uuid = str_replace('menu_link_content:', '', $parent);
$parent_menu_content = current(\Drupal::entityTypeManager()
  ->getStorage('menu_link_content')
  ->loadByProperties(array('uuid' => $uuid)));

$params = new MenuTreeParameters();
$params->setRoot($parent);
$params->setActiveTrail([$parent, reset($menu_links)->getPluginId()]);

$tree = $this->menuTree->load($parent, $params);
$built_tree = $this->menuTree->build($tree);

$link = end($parent_menu_content->link->getValue())['uri'];

$element = $this->menuTree->build($tree);
$build['#items'] = $element['#items'][$parent]['below'];

if ($link != 'route:<nolink>') {
  $build['#top_parent'] = $element['#items'][$parent];
}

if (empty($build['#cache']['contexts'])) {
  $build['#cache']['contexts'] = ['user.permissions'];
  $build['#cache']['tags'] = ['config:system.menu.main'];
}

return $build;

Aber die untergeordneten Elemente eines Links (in below) werden in Twig nicht korrekt gerendert, da er leer ist, und ich bin mir nicht sicher, ob es einen einfacheren Weg gibt, dies zu tun.

top_parent ist eine neue Themenvariable, die ich zu menu hinzugefügt habe, damit ich sie separat in Twig rendern kann.

Edit: below rendert jetzt richtig - ich habe vergessen {% import _self as menus %} in meinem zweiten Makro.

4
Kevin

Es wurde eine Möglichkeit gefunden, alle Fälle zu erfüllen, ohne mehrere Blöcke erstellen zu müssen (Sichtbarkeitsregeln wären ungerade) oder verschiedene Menüs zu erstellen. Ich konnte dies nicht erreichen, indem ich einen Inhaltstyp wie in der Frage annahm, da Sie denselben Inhaltstyp bis zu mehreren Bereichen des Menüs verschachtelt haben könnten und nicht wirklich wissen, ob die aktuelle Tiefe, in der Sie sich dafür befanden, als übergeordneter Inhaltstyp qualifiziert war (wo Der Menüblock sollte "beginnen" - nicht unbedingt das übergeordnete Element der Seite).

Ich beschloss, ein bisschen zu schummeln und die URL und das Alias-Muster zu verwenden. Wenn der aktuelle Pfad mit einem Muster übereinstimmte und die URL aus einer bestimmten Anzahl von Segmenten bestand, konnte ich feststellen, dass ich mich in einem bestimmten Teil des Menüs befand, in dem sich das Verhalten von anderen unterscheiden sollte. In diesem Fall sollte die aktuelle Seite und ihre untergeordneten Elemente anstelle des übergeordneten Elements der aktuellen Seite und ihrer Geschwister verwendet werden:

class MenuBlockEnhanced extends MenuBlock {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $node = \Drupal::routeMatch()->getParameter('node');

    // If we are not on a node route, get out.
    if (!$node) {
      return parent::build();
    }

    // If the menu being rendered is not specifically the sub nav, get out.
    if ($this->configuration["suggestion"] != 'subnav') {
      return parent::build();
    }

    $current_path = \Drupal::request()->getRequestUri();
    $parts = explode('/', $current_path);
    $build = parent::build();

    /** @var \Drupal\Core\Menu\MenuLinkManager $menu_link_manager */
    $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
    $route_params = array('node' => $node->id());
    $menu_links = $menu_link_manager->loadLinksByRoute('entity.node.canonical', $route_params, 'main');
    $set_parent = TRUE;

    $params = new MenuTreeParameters();

    // I don't really know of another way to 'tell' we are on a project, since
    // a project can have projects under it. We can only rely on the path and
    // how many 'parts' are in the path - anything over 4 parts would be anything
    // nested in this section.
    if (preg_match('/programs-projects/i', $current_path) && count($parts) == 4) {
      $parent = reset($menu_links)->getPluginId();
      $set_parent = FALSE;
    } else {
      $parent = reset($menu_links)->getParent();
    }

    $uuid = str_replace('menu_link_content:', '', $parent);
    $parent_menu_content = current(\Drupal::entityTypeManager()
     ->getStorage('menu_link_content')
     ->loadByProperties(array('uuid' => $uuid)));

    $params->setRoot($parent);
    $params->setActiveTrail([$parent, reset($menu_links)->getPluginId()]);
    $tree = $this->menuTree->load($parent, $params);

    // 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 = $this->menuTree->transform($tree, $manipulators);

    $link = end($parent_menu_content->link->getValue())['uri'];

    $element = $this->menuTree->build($tree);
    $build['#items'] = $element['#items'][$parent]['below'];

    if ($link != 'route:<nolink>' && $set_parent) {
      $build['#top_parent'] = $element['#items'][$parent];
    }

    if (empty($build['#cache']['contexts'])) {
      $build['#cache']['contexts'] = ['user.permissions'];
      $build['#cache']['tags'] = ['config:system.menu.main'];
    }

    return $build;
  }

}

Die Caching-Strategie kommt direkt von dem, was normalerweise vorhanden ist. Ehrlich gesagt muss ich sie wahrscheinlich nicht einmal angeben, da ich bereits parent::build() aufrufe und nur diesen Code reduziere.

In der Vorlage löse ich das Problem "Drucke ich nur Geschwister oder diese Seite und ihre untergeordneten Elemente?" Verwenden Sie die Prüfung in_active_path und fälschen Sie eine Kontrollunterbrechung in Twig Makro (siehe: break = true):

{% import _self as menus %}

{{ menus.menu_links(items, attributes, 0, top_parent) }}

{% macro menu_links(items, attributes, menu_level, top_parent) %}
  {% import _self as menus %}

  {% if items %}
    {% if top_parent %}
      <ul class="secondary-nav__list">
        {%
          set parent_list_classes =
          [
            'secondary-nav__item',
            'secondary-nav__item--parent'
          ]
        %}

        {% set parent_link_classes =
          [
            'link',
            'link--left-arrow',
          ]
        %}

        <li{{ top_parent.attributes.addClass(parent_list_classes) }}>
          {{ link(top_parent.title, top_parent.url, { 'class': parent_link_classes }) }}
          {{ menus.menu_child_links(items, attributes, 0) }}
        </li>
      </ul>
    {% else %}
      {{ menus.menu_child_links(items, attributes, 0) }}
    {% endif %}
  {% endif %}
{% endmacro %}

{% macro menu_child_links(items, attributes, menu_level) %}
  {% import _self as menus %}

  <ul class="secondary-nav__list">
    {% set break = false %}
    {% for item in items if not break %}
      {%
        set list_classes =
        [
          'secondary-nav__item',
          item.in_active_trail ? 'is-active'
        ]
      %}

      <li{{ item.attributes.addClass(list_classes) }}>
        {{ link(item.title, item.url) }}

        {% if item.below and item.in_active_trail %}
          {% set break = true %}
          {{ menus.menu_child_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
  </ul>
{% endmacro %}

Das Menü funktioniert in allen Fällen genau so, wie es verlangt wird, und geht bis zur maximalen Tiefe und sichert die Kette (wobei das übergeordnete blaue Glied oben angezeigt wird).

3
Kevin