รับพี่น้องลิงค์เมนู


11

ฉันกำลังพยายามสร้างเมนูใน Drupal 8 ที่เป็นเพียงลิงค์พี่น้องของหน้าปัจจุบัน ดังนั้นหากเมนูคือ:

  • บ้าน
  • ผู้ปกครอง 1
    • sub-parent 1
      • เด็ก 1
    • ผู้ปกครองย่อย 2
      • เด็ก 2
      • เด็ก 3
      • เด็ก 4
  • พ่อแม่ 2

เมื่อฉันอยู่ในหน้า "เด็ก 3" ฉันต้องการบล็อกเมนูเชื่อมโยงเพื่อให้มีลักษณะเช่นนี้:

  • เด็ก 2
  • เด็ก 3
  • เด็ก 4

ฉันรู้วิธีการทำสิ่งนี้ใน D7 ฉันคิดว่า แต่ฉันมีความยากลำบากในการแปลความรู้นั้นเป็น D8 นี่เป็นสิ่งที่สามารถทำได้ใน D8 หรือไม่? และถ้าเป็นเช่นนั้นใครบางคนสามารถชี้แนะทิศทางที่ถูกต้องให้ฉันได้

ขอบคุณ!

Clarification: ระดับลูกจำเป็นต้องแปรเปลี่ยนเพื่อให้รายการเมนูที่มีความลึกต่างกันสามารถแสดงพี่น้องได้ ตัวอย่างเช่นนอกเหนือจากการต้องการเมนูสำหรับเด็กฉันต้องมีเมนูสำหรับผู้ปกครองย่อยและเมนูสำหรับผู้ปกครองเป็นต้น ฉันยังไม่สามารถควบคุมได้ / รู้ว่าเมนูจะลึกแค่ไหนและถ้ามันลึกลงไปในทุกส่วน

คำตอบ:


19

ดังนั้นฉันจึงหารหัสที่จะให้ฉันทำโดยการสร้างบล็อกที่กำหนดเองและในวิธีการสร้างออกเมนูด้วยการเพิ่มหม้อแปลง นี้คือการเชื่อมโยงที่ผมใช้จะคิดออกว่าจะได้รับเมนูในบล็อกและเพิ่มหม้อแปลงไป: http://alexrayu.com/blog/drupal-8-display-submenu-block build()ท้ายสุดของฉันดูเหมือนว่า:

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

หม้อแปลงเป็นบริการดังนั้นฉันจึงเพิ่มintranet.services.ymlโมดูลอินทราเน็ตของฉันชี้ไปที่คลาสที่ฉันสิ้นสุดการกำหนด คลาสมีสามวิธี: removeInactiveTrail()ซึ่งเรียกgetCurrentParent()ให้พาเรนต์ของเพจที่ผู้ใช้ใช้อยู่ในขณะนี้และstripChildren()ซึ่งปล้นเมนูลงไปเฉพาะรายการย่อยของรายการเมนูปัจจุบันและพี่น้องของมัน (เช่น: ลบเมนูย่อยทั้งหมดที่ไม่ได้ ' t ในเส้นทางที่ใช้งาน)

นี่คือสิ่งที่ดูเหมือน:

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

นี่เป็นวิธีที่ดีที่สุดที่จะทำ? อาจจะไม่. แต่อย่างน้อยก็เป็นจุดเริ่มต้นสำหรับผู้ที่ต้องการทำสิ่งที่คล้ายกัน


นี่เป็นสิ่งที่สิ่งที่เท้าทำ +1 สำหรับการใช้บริการ menu.tree
mradcliffe

2
คุณช่วยบอกได้ไหมว่าควรใส่รหัสอะไรในไฟล์ service.yml วิธีชี้คลาสจากไฟล์ service.yml ได้อย่างไร
siddiq

จะยกเว้นลิงค์เมนูหลักได้อย่างไร?
Permana

3

Drupal 8 มีฟังก์ชั่นเมนูบล็อกที่สร้างขึ้นในแกนกลางสิ่งเดียวที่คุณต้องทำคือการสร้างบล็อกเมนูใหม่ใน Block Ui และกำหนดค่านั้น

ที่เกิดขึ้นโดย:

  • วางบล็อกใหม่แล้วเลือกเมนูที่คุณต้องการสร้างบล็อก
  • ในการกำหนดค่าบล็อกคุณต้องเลือก "ระดับเมนูเริ่มต้น" เป็น 3
  • คุณอาจต้องการตั้งค่า "จำนวนเมนูสูงสุดที่จะแสดง" เป็น 1 ในกรณีที่คุณต้องการพิมพ์รายการเมนูจากระดับที่สามเท่านั้น

น่าเสียดายที่ฉันไม่สามารถแน่ใจได้ว่าระดับหน้าจะอยู่ที่ดังนั้นฉันไม่สามารถเพียงแค่สร้างบล็อกเมนูสำหรับมัน นอกจากนี้ยังมีความเป็นไปได้ที่มันอาจจะต้องเป็นระดับตัวแปรขึ้นอยู่กับสิ่งที่โครงสร้างของเว็บไซต์เป็น
Erin McLaughlin

menu_block สำหรับ Drupal 8 ไม่ได้รวมฟังก์ชั่นการใช้งานเพื่อติดตามโหนดปัจจุบัน, แพตช์ที่ตรวจสอบที่นี่; drupal.org/node/2756675
คริสเตียน

ตกลงสำหรับการใช้งานแบบคงที่ แต่ไม่ใช่สำหรับการใช้งานแบบไดนามิกเช่นเดียวกับใน "วางบล็อกบนทุกหน้าและแสดงพี่น้องของหน้าปัจจุบันไม่ว่าคุณจะอยู่ในระดับใด"
leymannx

3

การตั้งค่ารูทบนลิงค์ปัจจุบันอาจใช้เทคนิค:

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

ไม่น่าเสียดายที่นี่มี แต่เด็กเท่านั้น แต่ไม่ใช่พี่น้อง OP ต้องการพี่น้อง
leymannx

3

บล็อกเมนูพี่น้อง

ด้วยความช่วยเหลือของ @Icubes คำตอบและMenuLinkTreeInterface::getCurrentRouteMenuTreeParametersเราสามารถรับเมนูเส้นทางของเส้นทางปัจจุบันได้ มีว่าเรายังมีรายการเมนูหลัก การตั้งค่าให้เป็นจุดเริ่มต้นผ่านMenuTreeParameters::setRootการสร้างต้นไม้ใหม่ให้เมนูพี่น้องที่คุณต้องการ

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

วิธีนี้ใช้ได้ผลเหมือนมีเสน่ห์ :)
Pankaj Sachdeva
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.