메뉴 링크 형제 가져 오기


11

Drupal 8에서 현재 페이지의 링크를 링크하는 메뉴를 만들려고합니다. 따라서 메뉴가 다음과 같은 경우

  • 부모 1
    • 소 부모 1
      • 아이 1
    • 친부모 2
      • 아이 2
      • 아이 3
      • 어린이 4
  • 부모 2

"Child 3"페이지에있을 때 메뉴 블록을 다음과 같이 링크하려고합니다.

  • 아이 2
  • 아이 3
  • 어린이 4

D7 에서이 작업을 수행하는 방법을 알고 있지만 그 지식을 D8로 변환하는 데 어려움을 겪고 있습니다. 이것도 D8에서 할 수있는 일입니까? 그리고 만약 그렇다면 누군가 어떻게해야하는지 올바른 방향으로 안내해 줄 수 있습니까?

감사!

설명 : 하위 수준은 가변적이어야하므로 깊이가 다른 메뉴 항목에 형제가 표시 될 수 있습니다. 예를 들어, 아이들을위한 메뉴를 원할뿐 아니라, 부모님을위한 메뉴와 부모님을위한 메뉴가 필요합니다. 또한 메뉴가 얼마나 깊고 모든 섹션에서 깊숙이 들어가는 지에 대한 제어 / 지식을 가지고 있지 않습니다.

답변:


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

이것이 최선의 방법입니까? 아마 아닙니다. 그러나 적어도 비슷한 것을 해야하는 사람들에게는 시작 점이됩니다.


이것은 footermap이하는 일과 거의 같습니다. menu.tree 서비스 사용시 +1
mradcliffe

2
service.yml 파일에 어떤 코드를 넣어야하는지 말씀해 주시겠습니까? service.yml 파일에서 클래스를 가리키는 방법은 무엇입니까?
siddiq

부모 메뉴 링크를 제외하는 방법?
Permana

3

Drupal 8의 핵심 기능에는 Menu Block 기능이 내장되어 있으며, 블록 Ui에서 새 메뉴 블록을 생성하고 구성하는 것입니다.

그것은 다음과 같이 일어난다 :

  • 새 블록을 배치 한 다음 블록을 만들 메뉴를 선택하십시오.
  • 블록 구성에서 "초기 메뉴 레벨"을 3으로 선택해야합니다.
  • 세 번째 수준의 메뉴 항목 만 인쇄하려는 경우 "표시 할 최대 메뉴 수준 수"를 1로 설정할 수도 있습니다.

불행히도, 나는 페이지가 어느 레벨에 있는지 확신 할 수 없으므로 메뉴 블록을 만들 수는 없습니다. 또한 사이트 구조가 무엇인지에 따라 가변 레벨이어야 할 수도 있습니다.
Erin McLaughlin

Drupal 8의 menu_block에는 현재 현재 노드를 따르는 기능이 포함되어 있지 않습니다. drupal.org/node/2756675
Christian

정적 사용이 가능합니다. 그러나 "모든 페이지에 블록을 배치하고 현재 레벨에 관계없이 현재 페이지의 형제를 표시"와 같이 동적으로 사용하기에는 적합하지 않습니다.
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.