You are here

public function MenuBlock::build in Menu Block 8

Builds and returns the renderable array for this block plugin.

If a block should not be rendered because it has no content, then this method must also ensure to return no content: it must then only return an empty array, or an empty array with #cache set (with cacheability metadata indicating the circumstances for it being empty).

Return value

array A renderable array representing the content of the block.

Overrides SystemMenuBlock::build

See also

\Drupal\block\BlockViewBuilder

1 call to MenuBlock::build()
MenuBlock::blockAccess in src/Plugin/Block/MenuBlock.php
Indicates whether the block should be shown.

File

src/Plugin/Block/MenuBlock.php, line 222

Class

MenuBlock
Provides an extended Menu block.

Namespace

Drupal\menu_block\Plugin\Block

Code

public function build() {
  $menu_name = $this
    ->getDerivativeId();
  $parameters = $this->menuTree
    ->getCurrentRouteMenuTreeParameters($menu_name);

  // Adjust the menu tree parameters based on the block's configuration.
  $level = $this->configuration['level'];
  $depth = $this->configuration['depth'];

  // For blocks placed in Layout Builder or similar, check for the deprecated
  // 'expand' config property in case the menu block's configuration has not
  // yet been updated.
  $expand_all_items = $this->configuration['expand'] ?? $this->configuration['expand_all_items'];
  $parent = $this->configuration['parent'];
  $follow = $this->configuration['follow'];
  $follow_parent = $this->configuration['follow_parent'];
  $following = FALSE;
  $parameters
    ->setMinDepth($level);

  // If we're following the active trail and the active trail is deeper than
  // the initial starting level, we update the level to match the active menu
  // item's level in the menu.
  if ($follow && count($parameters->activeTrail) > $level) {
    $level = count($parameters->activeTrail);
    $following = TRUE;
  }

  // When the depth is configured to zero, there is no depth limit. When depth
  // is non-zero, it indicates the number of levels that must be displayed.
  // Hence this is a relative depth that we must convert to an actual
  // (absolute) depth, that may never exceed the maximum depth.
  if ($depth > 0) {
    $parameters
      ->setMaxDepth(min($level + $depth - 1, $this->menuTree
      ->maxDepth()));
  }

  // If we're currently following an active menu item, or for menu blocks with
  // start level greater than 1, only show menu items from the current active
  // trail. Adjust the root according to the current position in the menu in
  // order to determine if we can show the subtree. If we're not following an
  // active trail and using a fixed parent item, we'll skip this step.
  $fixed_parent_menu_link_id = str_replace($menu_name . ':', '', $parent);
  if ($following || $level > 1 && !$fixed_parent_menu_link_id) {
    if (count($parameters->activeTrail) >= $level) {

      // Active trail array is child-first. Reverse it, and pull the new menu
      // root based on the parent of the configured start level.
      $menu_trail_ids = array_reverse(array_values($parameters->activeTrail));
      $offset = $following && $follow_parent == 'active' ? 2 : 1;
      $menu_root = $menu_trail_ids[$level - $offset];
      $parameters
        ->setRoot($menu_root)
        ->setMinDepth(1);
      if ($depth > 0) {
        $parameters
          ->setMaxDepth(min($depth, $this->menuTree
          ->maxDepth()));
      }
    }
    else {
      return [];
    }
  }

  // If expandedParents is empty, the whole menu tree is built.
  if ($expand_all_items) {
    $parameters->expandedParents = [];
  }

  // When a fixed parent item is set, root the menu tree at the given ID.
  if ($fixed_parent_menu_link_id) {

    // Clone the parameters so we can fall back to using them if we're
    // following the active menu item and the current page is part of the
    // active menu trail.
    $fixed_parameters = clone $parameters;
    $fixed_parameters
      ->setRoot($fixed_parent_menu_link_id);
    $tree = $this->menuTree
      ->load($menu_name, $fixed_parameters);

    // Check if the tree contains links.
    if (empty($tree)) {

      // If the starting level is 1, we always want the child links to appear,
      // but the requested tree may be empty if the tree does not contain the
      // active trail. We're accessing the configuration directly since the
      // $level variable may have changed by this point.
      if ($this->configuration['level'] === 1 || $this->configuration['level'] === '1') {

        // Change the request to expand all children and limit the depth to
        // the immediate children of the root.
        $fixed_parameters->expandedParents = [];
        $fixed_parameters
          ->setMinDepth(1);
        $fixed_parameters
          ->setMaxDepth(1);

        // Re-load the tree.
        $tree = $this->menuTree
          ->load($menu_name, $fixed_parameters);
      }
    }
    elseif ($following) {

      // If we're following the active menu item, and the tree isn't empty
      // (which indicates we're currently in the active trail), we unset
      // the tree we made and just let the active menu parameters from before
      // do their thing.
      unset($tree);
    }
  }

  // Load the tree if we haven't already.
  if (!isset($tree)) {
    $tree = $this->menuTree
      ->load($menu_name, $parameters);
  }
  $manipulators = [
    [
      'callable' => 'menu.default_tree_manipulators:checkAccess',
    ],
    [
      'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
    ],
  ];
  $tree = $this->menuTree
    ->transform($tree, $manipulators);
  $build = $this->menuTree
    ->build($tree);
  $label = $this
    ->getBlockLabel() ?: $this
    ->label();

  // Set the block's #title (label) to the dynamic value.
  $build['#title'] = [
    '#markup' => $label,
  ];
  if (!empty($build['#theme'])) {

    // Add the configuration for use in menu_block_theme_suggestions_menu().
    $build['#menu_block_configuration'] = $this->configuration;

    // Set the generated label into the configuration array so it is
    // propagated to the theme preprocessor and template(s) as needed.
    $build['#menu_block_configuration']['label'] = $label;

    // Remove the menu name-based suggestion so we can control its precedence
    // better in menu_block_theme_suggestions_menu().
    $build['#theme'] = 'menu';
  }
  $build['#contextual_links']['menu'] = [
    'route_parameters' => [
      'menu' => $menu_name,
    ],
  ];
  return $build;
}