Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 101 |
BlockViewBuilder | |
0.00% |
0 / 1 |
|
0.00% |
0 / 8 |
306 | |
0.00% |
0 / 101 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
createInstance | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 7 |
|||
buildComponents | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
view | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
viewMultiple | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 30 |
|||
buildPreRenderableBlock | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 31 |
|||
lazyBuilder | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
preRender | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 24 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\block\BlockViewBuilder. | |
*/ | |
namespace Drupal\block; | |
use Drupal\Core\Block\MainContentBlockPluginInterface; | |
use Drupal\Core\Block\TitleBlockPluginInterface; | |
use Drupal\Core\Cache\Cache; | |
use Drupal\Core\Cache\CacheableMetadata; | |
use Drupal\Core\Entity\EntityManagerInterface; | |
use Drupal\Core\Entity\EntityTypeInterface; | |
use Drupal\Core\Entity\EntityViewBuilder; | |
use Drupal\Core\Entity\EntityInterface; | |
use Drupal\Core\Extension\ModuleHandlerInterface; | |
use Drupal\Core\Language\LanguageManagerInterface; | |
use Drupal\Core\Plugin\ContextAwarePluginInterface; | |
use Drupal\Core\Render\Element; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
/** | |
* Provides a Block view builder. | |
*/ | |
class BlockViewBuilder extends EntityViewBuilder { | |
/** | |
* The module handler. | |
* | |
* @var \Drupal\Core\Extension\ModuleHandlerInterface | |
*/ | |
protected $moduleHandler; | |
/** | |
* Constructs a new BlockViewBuilder. | |
* | |
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | |
* The entity type definition. | |
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
* The entity manager service. | |
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
* The language manager. | |
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
* The module handler. | |
*/ | |
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler) { | |
parent::__construct($entity_type, $entity_manager, $language_manager); | |
$this->moduleHandler = $module_handler; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { | |
return new static( | |
$entity_type, | |
$container->get('entity.manager'), | |
$container->get('language_manager'), | |
$container->get('module_handler') | |
); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function buildComponents(array &$build, array $entities, array $displays, $view_mode) { | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { | |
$build = $this->viewMultiple(array($entity), $view_mode, $langcode); | |
return reset($build); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { | |
/** @var \Drupal\block\BlockInterface[] $entities */ | |
$build = array(); | |
foreach ($entities as $entity) { | |
$entity_id = $entity->id(); | |
$plugin = $entity->getPlugin(); | |
$cache_tags = Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags()); | |
$cache_tags = Cache::mergeTags($cache_tags, $plugin->getCacheTags()); | |
// Create the render array for the block as a whole. | |
// @see template_preprocess_block(). | |
$build[$entity_id] = array( | |
'#cache' => [ | |
'keys' => ['entity_view', 'block', $entity->id()], | |
'contexts' => Cache::mergeContexts( | |
$entity->getCacheContexts(), | |
$plugin->getCacheContexts() | |
), | |
'tags' => $cache_tags, | |
'max-age' => $plugin->getCacheMaxAge(), | |
], | |
'#weight' => $entity->getWeight(), | |
); | |
// Allow altering of cacheability metadata or setting #create_placeholder. | |
$this->moduleHandler->alter(['block_build', "block_build_" . $plugin->getBaseId()], $build[$entity_id], $plugin); | |
if ($plugin instanceof MainContentBlockPluginInterface || $plugin instanceof TitleBlockPluginInterface) { | |
// Immediately build a #pre_render-able block, since this block cannot | |
// be built lazily. | |
$build[$entity_id] += static::buildPreRenderableBlock($entity, $this->moduleHandler()); | |
} | |
else { | |
// Assign a #lazy_builder callback, which will generate a #pre_render- | |
// able block lazily (when necessary). | |
$build[$entity_id] += [ | |
'#lazy_builder' => [static::class . '::lazyBuilder', [$entity_id, $view_mode, $langcode]], | |
]; | |
} | |
} | |
return $build; | |
} | |
/** | |
* Builds a #pre_render-able block render array. | |
* | |
* @param \Drupal\block\BlockInterface $entity | |
* A block config entity. | |
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
* The module handler service. | |
* | |
* @return array | |
* A render array with a #pre_render callback to render the block. | |
*/ | |
protected static function buildPreRenderableBlock($entity, ModuleHandlerInterface $module_handler) { | |
$plugin = $entity->getPlugin(); | |
$plugin_id = $plugin->getPluginId(); | |
$base_id = $plugin->getBaseId(); | |
$derivative_id = $plugin->getDerivativeId(); | |
$configuration = $plugin->getConfiguration(); | |
// Inject runtime contexts. | |
if ($plugin instanceof ContextAwarePluginInterface) { | |
$contexts = \Drupal::service('context.repository')->getRuntimeContexts($plugin->getContextMapping()); | |
\Drupal::service('context.handler')->applyContextMapping($plugin, $contexts); | |
} | |
// Create the render array for the block as a whole. | |
// @see template_preprocess_block(). | |
$build = [ | |
'#theme' => 'block', | |
'#attributes' => [], | |
// All blocks get a "Configure block" contextual link. | |
'#contextual_links' => [ | |
'block' => [ | |
'route_parameters' => ['block' => $entity->id()], | |
], | |
], | |
'#weight' => $entity->getWeight(), | |
'#configuration' => $configuration, | |
'#plugin_id' => $plugin_id, | |
'#base_plugin_id' => $base_id, | |
'#derivative_plugin_id' => $derivative_id, | |
'#id' => $entity->id(), | |
'#pre_render' => [ | |
static::class . '::preRender', | |
], | |
// Add the entity so that it can be used in the #pre_render method. | |
'#block' => $entity, | |
]; | |
// If an alter hook wants to modify the block contents, it can append | |
// another #pre_render hook. | |
$module_handler->alter(['block_view', "block_view_$base_id"], $build, $plugin); | |
return $build; | |
} | |
/** | |
* #lazy_builder callback; builds a #pre_render-able block. | |
* | |
* @param $entity_id | |
* A block config entity ID. | |
* @param $view_mode | |
* The view mode the block is being viewed in. | |
* | |
* @return array | |
* A render array with a #pre_render callback to render the block. | |
*/ | |
public static function lazyBuilder($entity_id, $view_mode) { | |
return static::buildPreRenderableBlock(entity_load('block', $entity_id), \Drupal::service('module_handler')); | |
} | |
/** | |
* #pre_render callback for building a block. | |
* | |
* Renders the content using the provided block plugin, and then: | |
* - if there is no content, aborts rendering, and makes sure the block won't | |
* be rendered. | |
* - if there is content, moves the contextual links from the block content to | |
* the block itself. | |
*/ | |
public static function preRender($build) { | |
$content = $build['#block']->getPlugin()->build(); | |
// Remove the block entity from the render array, to ensure that blocks | |
// can be rendered without the block config entity. | |
unset($build['#block']); | |
if ($content !== NULL && !Element::isEmpty($content)) { | |
// 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, or alternate ones used by | |
// alternate block rendering approaches in contrib (for instance, Panels). | |
// However, the use of a child element is an implementation detail of this | |
// particular block rendering approach. 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 from $content and merge them into the | |
// top-level element. | |
foreach (array('#attributes', '#contextual_links') as $property) { | |
if (isset($content[$property])) { | |
$build[$property] += $content[$property]; | |
unset($content[$property]); | |
} | |
} | |
$build['content'] = $content; | |
} | |
// Either the block's content is completely empty, or it consists only of | |
// cacheability metadata. | |
else { | |
// Abort rendering: render as the empty string and ensure this block is | |
// render cached, so we can avoid the work of having to repeatedly | |
// determine whether the block is empty. For instance, modifying or adding | |
// entities could cause the block to no longer be empty. | |
$build = array( | |
'#markup' => '', | |
'#cache' => $build['#cache'], | |
); | |
// If $content is not empty, then it contains cacheability metadata, and | |
// we must merge it with the existing cacheability metadata. This allows | |
// blocks to be empty, yet still bubble cacheability metadata, to indicate | |
// why they are empty. | |
if (!empty($content)) { | |
CacheableMetadata::createFromRenderArray($build) | |
->merge(CacheableMetadata::createFromRenderArray($content)) | |
->applyTo($build); | |
} | |
} | |
return $build; | |
} | |
} |