pluginManagerBlock = $plugin_manager_block; $this->contextRepository = $context_repository; $this->contextHandler = $context_handler; $this->account = $account; $this->requestStack = $request_stack; $this->routeMatch = $route_match; $this->titleResolver = $title_resolver; } /** * Builds the render array for a block. * * @param string $id * The string of block plugin to render. * @param array $configuration * (optional) Pass on any configuration to the plugin block. * @param bool $wrapper * (optional) Whether or not use block template for rendering. * * @return array * A renderable array representing the content of the block. */ public function build(string $id, array $configuration = [], bool $wrapper = TRUE): array { $configuration += ['label_display' => BlockPluginInterface::BLOCK_LABEL_VISIBLE]; /** @var \Drupal\Core\Block\BlockPluginInterface $block_plugin */ $block_plugin = $this->pluginManagerBlock->createInstance($id, $configuration); // Inject runtime contexts. if ($block_plugin instanceof ContextAwarePluginInterface) { $contexts = $this->contextRepository->getRuntimeContexts($block_plugin->getContextMapping()); $this->contextHandler->applyContextMapping($block_plugin, $contexts); } $build = []; $access = $block_plugin->access($this->account, TRUE); if ($access->isAllowed()) { // Title block needs a special treatment. if ($block_plugin instanceof TitleBlockPluginInterface) { $request = $this->requestStack->getCurrentRequest(); $title = $this->titleResolver->getTitle($request, $this->routeMatch->getRouteObject()); $block_plugin->setTitle($title); } // Place the content returned by the block plugin into a 'content' child // element, as a way to allow the plugin to have complete control of its // properties and rendering (for instance, its own #theme) without // conflicting with the properties used above. $build['content'] = $block_plugin->build(); if ($block_plugin instanceof TitleBlockPluginInterface) { $build['content']['#cache']['contexts'][] = 'url'; } // Some blocks returns NULL instead of array when empty. // @see https://www.drupal.org/project/drupal/issues/3212354 if ($wrapper && is_array($build['content']) && !Element::isEmpty($build['content'])) { $build += [ '#theme' => 'block', '#id' => $configuration['id'] ?? NULL, '#attributes' => [], '#contextual_links' => [], '#configuration' => $block_plugin->getConfiguration(), '#plugin_id' => $block_plugin->getPluginId(), '#base_plugin_id' => $block_plugin->getBaseId(), '#derivative_plugin_id' => $block_plugin->getDerivativeId(), ]; // Semantically, the content returned by the plugin is the block, and in // particular, #attributes and #contextual_links is information about // the *entire* block. Therefore, we must move these properties into the // top-level element. foreach (['#attributes', '#contextual_links'] as $property) { if (isset($build['content'][$property])) { $build[$property] = $build['content'][$property]; unset($build['content'][$property]); } } } } CacheableMetadata::createFromRenderArray($build) ->merge(CacheableMetadata::createFromObject($access)) ->merge(CacheableMetadata::createFromObject($block_plugin)) ->applyTo($build); if (!isset($build['#cache']['keys'])) { $build['#cache']['keys'] = [ 'twig_tweak_block', $id, '[configuration]=' . hash('sha256', serialize($configuration)), '[wrapper]=' . (int) $wrapper, ]; } return $build; } }