Nhận liên kết thực đơn anh chị em


11

Tôi đang cố gắng tạo một menu trong Drupal 8 chỉ là các liên kết anh chị của trang hiện tại. Vì vậy, nếu menu là:

  • Trang Chủ
  • Phụ huynh 1
    • Phụ huynh 1
      • Con 1
    • Phụ huynh 2
      • Con 2
      • Con 3
      • Con 4
  • Cha mẹ 2

Khi tôi ở trang "Con 3", tôi muốn một khối menu liên kết để trông như thế này:

  • Con 2
  • Con 3
  • Con 4

Tôi biết làm thế nào để làm điều này trong D7, tôi nghĩ vậy, nhưng tôi đang gặp khó khăn trong việc dịch kiến ​​thức đó sang D8. Đây thậm chí là một cái gì đó có thể làm được trong D8? Và nếu có, ai đó có thể chỉ cho tôi đi đúng hướng về cách làm không?

Cảm ơn!

Làm rõ: Cấp độ con cần phải thay đổi, để các mục menu có độ sâu khác nhau có thể hiển thị anh chị em của chúng. Vì vậy, ví dụ, ngoài việc muốn có một thực đơn cho trẻ em, tôi cần một thực đơn cho cha mẹ phụ và một thực đơn cho cha mẹ và vv. Tôi cũng không kiểm soát được / kiến ​​thức về độ sâu của menu và nếu nó đi sâu vào tất cả các phần.

Câu trả lời:


19

Vì vậy, cuối cùng tôi đã tìm ra một số mã cho phép tôi làm điều này, bằng cách tạo một khối tùy chỉnh và, trong phương thức xây dựng, xuất ra menu với các biến áp được thêm vào nó. Đây là liên kết tôi đã sử dụng để tìm ra cách lấy menu trong khối và thêm biến áp vào đó: http://alexrayu.com/blog/drupal-8-display-submenu-block . Cuối build()cùng của tôi trông như thế này:

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

Biến áp là một dịch vụ, vì vậy tôi đã thêm một intranet.services.ymlmô-đun mạng nội bộ của mình, chỉ vào lớp mà cuối cùng tôi đã xác định. Lớp này có ba phương thức : removeInactiveTrail(), gọi là getCurrentParent()để lấy cha mẹ của trang mà người dùng hiện đang sử dụng và stripChildren()đã loại bỏ menu xuống chỉ cho con của mục menu hiện tại và anh chị em của nó (ví dụ: đã xóa tất cả các menu con mà ' t trong đường mòn hoạt động).

Đây là những gì trông giống như:

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

Đây có phải là cách tốt nhất để làm điều đó? Chắc là không. Nhưng nó ít nhất cung cấp một nơi bắt đầu cho những người cần phải làm một cái gì đó tương tự.


Đây là khá nhiều những gì footermap làm. +1 để sử dụng dịch vụ menu.tree.
mradcliffe

2
Bạn có thể vui lòng cho biết mã nào nên được đặt trong tệp service.yml không? Làm thế nào để trỏ một lớp từ tệp service.yml?
siddiq

Làm thế nào để loại trừ liên kết menu cha / s?
Permana

3

Drupal 8 có chức năng Khối Menu được xây dựng trong lõi, điều duy nhất bạn phải làm là tạo một khối menu mới trong Khối Ui và định cấu hình đó.

Điều đó xảy ra bởi:

  • Đặt một khối mới và sau đó chọn menu bạn muốn tạo một khối.
  • Trong cấu hình khối, bạn phải chọn "Cấp menu ban đầu" là 3.
  • Bạn cũng có thể muốn đặt "Số mức menu tối đa hiển thị" thành 1 trong trường hợp bạn chỉ muốn in các mục menu từ cấp thứ ba.

Thật không may, tôi không thể chắc chắn trang sẽ ở cấp nào, vì vậy tôi không thể tạo một khối menu cho nó. Cũng có khả năng nó có thể cần phải ở các mức khác nhau, tùy thuộc vào cấu trúc trang web cuối cùng là gì.
Erin McLaughlin

menu_block cho Drupal 8 hiện không bao gồm chức năng theo dõi nút hiện tại, các bản vá được xem xét ở đây; drupal.org/node/2756675
Christian

OK để sử dụng tĩnh. Nhưng không phải để sử dụng động như trong "Đặt một khối trên mỗi trang và hiển thị anh chị em của trang hiện tại cho dù bạn đang ở cấp độ nào."
leymannx

3

Đặt gốc trên liên kết hiện tại có thể thực hiện các thủ thuật:

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

Không, thật không may, điều này chỉ cho thấy trẻ em. Nhưng không phải anh em ruột thịt. OP muốn anh chị em.
leymannx

3

Anh chị em khối

Với sự giúp đỡ của câu trả lời @Icubes và MenuLinkTreeInterface::getCurrentRouteMenuTreeParameterschúng tôi chỉ đơn giản có thể nhận được dấu vết menu hoạt động của tuyến đường hiện tại. Có điều đó chúng tôi cũng có các mục menu cha. Đặt làm điểm bắt đầu thông qua MenuTreeParameters::setRootđể xây dựng một cây mới cung cấp cho bạn menu anh chị em mong muốn.

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

Giải pháp này làm việc như một lá bùa. :)
Pankaj Sachdeva
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.