Dapatkan saudara tautan menu

11

Saya mencoba membuat menu di Drupal 8 yang hanya merupakan tautan saudara dari halaman saat ini. Jadi jika menunya adalah:

  • Rumah
  • Induk 1
    • Sub-induk 1
      • Anak 1
    • Sub-induk 2
      • Anak 2
      • Anak 3
      • Anak 4
  • Induk 2

Ketika saya di halaman "Anak 3", saya ingin blok menu untuk menautkan terlihat seperti ini:

  • Anak 2
  • Anak 3
  • Anak 4

Saya tahu bagaimana melakukan ini di D7, saya pikir, tapi saya kesulitan menerjemahkan pengetahuan itu ke D8. Apakah ini bahkan sesuatu yang bisa dilakukan di D8? Dan jika ya, bisakah seseorang mengarahkan saya ke arah yang benar tentang cara melakukannya?

Terima kasih!

Klarifikasi: Tingkat anak harus bervariasi, sehingga item menu dengan kedalaman berbeda dapat menampilkan saudara mereka. Jadi misalnya, selain menginginkan menu untuk anak-anak, saya memerlukan menu untuk sub-orangtua dan menu untuk orangtua dan sebagainya. Saya juga tidak memiliki kendali atas / pengetahuan tentang seberapa dalam menu berjalan dan jika itu berjalan jauh di semua bagian.

Erin McLaughlin
sumber

Jawaban:

19

Jadi, saya akhirnya mencari tahu beberapa kode yang akan membiarkan saya melakukan ini, dengan membuat blok khusus dan, dalam metode build, mengeluarkan menu dengan menambahkan transformer ke dalamnya. Ini adalah tautan yang saya gunakan untuk mencari tahu cara mendapatkan menu di blok dan menambahkan transformer ke dalamnya: http://alexrayu.com/blog/drupal-8-display-submenu-block . Saya build()akhirnya terlihat seperti ini:

$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 adalah layanan, jadi saya menambahkan intranet.services.ymlke modul intranet saya, menunjuk ke kelas yang akhirnya saya definisikan. Kelas memiliki tiga metode:, removeInactiveTrail()yang dipanggil getCurrentParent()untuk mendapatkan induk dari halaman saat ini pengguna berada, dan stripChildren(), yang menanggalkan menu ke hanya anak-anak dari item menu saat ini dan saudara kandungnya (yaitu: menghapus semua submenu yang bukan t dalam jejak aktif).

Seperti inilah bentuknya:

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

Apakah ini cara terbaik untuk melakukannya? Mungkin tidak. Tapi itu setidaknya menyediakan tempat awal bagi orang yang perlu melakukan hal serupa.

Erin McLaughlin
sumber
Inilah yang dilakukan pijakan. +1 untuk menggunakan layanan menu.tree.
mradcliffe
2
Bisakah Anda memberi tahu kode apa yang harus ditempatkan dalam file service.yml? Bagaimana cara menunjuk kelas dari file service.yml?
siddiq
Bagaimana cara mengecualikan tautan menu orang tua?
Permana
3

Drupal 8 memiliki fungsi Blok Menu yang dibangun pada inti, satu-satunya hal yang harus Anda lakukan adalah membuat blok menu baru di Blok Ui dan mengonfigurasinya.

Itu terjadi oleh:

  • Menempatkan blok baru dan kemudian memilih menu yang Anda ingin buat blok.
  • Dalam konfigurasi blok Anda harus memilih "Level menu awal" menjadi 3.
  • Anda mungkin juga ingin mengatur "Jumlah maksimum level menu untuk ditampilkan" menjadi 1 jika Anda hanya ingin mencetak item menu dari level ketiga.
lauriii
sumber
Sayangnya, saya tidak bisa memastikan level halaman yang akan dituju, jadi saya tidak bisa hanya membuat blok menu untuk itu. Ada juga kemungkinan bahwa itu mungkin perlu tingkat variabel, tergantung pada apa struktur situs akhirnya.
Erin McLaughlin
menu_block untuk Drupal 8 saat ini tidak menyertakan fungsionalitas untuk mengikuti simpul saat ini, tambalan yang ditinjau di sini; drupal.org/node/2756675
Christian
OK untuk penggunaan statis. Tetapi tidak untuk penggunaan dinamis seperti pada "Tempatkan blok pada setiap halaman dan tunjukkan saudara kandung halaman saat ini tidak peduli apa pun level Anda saat ini."
leymannx
3

Mengatur root pada tautan saat ini mungkin melakukan trik:

$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
sumber
Nah, sayangnya ini hanya menunjukkan anak-anak. Tapi bukan saudara kandung. OP menginginkan saudara kandung.
leymannx
3

Blok Menu Saudara

Dengan bantuan jawaban @Icubes dan MenuLinkTreeInterface::getCurrentRouteMenuTreeParameterskami cukup mendapatkan jejak menu aktif rute saat ini. Setelah itu kami juga memiliki item menu induk. Pengaturan bahwa sebagai titik awal melalui MenuTreeParameters::setRootuntuk membangun pohon baru memberi Anda menu saudara yang diinginkan.

// 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
sumber
Solusi ini bekerja seperti pesona. :)
Pankaj Sachdeva