diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index 9aa4b87..b50883c 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -204,15 +204,16 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // Is this placeholder eligible because it is either: // - Cached and therefore cache retrievable? // - Or has a #pre_render_cache callback? - if (!isset($elements['#cache_no_placeholder']) && ($cached_element || isset($pre_bubbling_elements['#pre_render']))) { + if (!isset($elements['#cache_no_placeholder']) && ($cached_element || isset($pre_bubbling_elements['#pre_render_cache']))) { // Do we _need_ to create a placeholder? if (isset($elements['#cache']['max-age']) && $elements['#cache']['max-age'] == 0) { + $render_cache_array = array_intersect_key($pre_bubbling_elements, ['#cache' => 0, '#pre_render_cache' => 1]); $elements = []; // @todo Add helper function for this based on the render array. // @todo Use hash of $pre_bubbling_elements['#pre_render_cache','#cache']? - $pholder_id = \Drupal\Component\Utility\Html::getId('X-FF-PH--' . implode(':', $pre_bubbling_elements['#cache']['keys'])); + $pholder_id = \Drupal\Component\Utility\Html::getId('X-FF-PH--' . implode(':', $render_cache_array['#cache']['keys'])); $elements['#markup'] = '
'; - $elements['#cache']['placeholders'][$pholder_id] = $pre_bubbling_elements; + $elements['#cache']['placeholders'][$pholder_id] = $render_cache_array; if ($cached_element) { // @todo If we already have data, then store it in the renderer. } @@ -240,6 +241,16 @@ protected function doRender(&$elements, $is_root_call = FALSE) { } } + // Ensure the item we want to render is loaded. + if (!isset($elements['#pre_render']) && isset($elements['#pre_render_cache'])) { + foreach ($elements['#pre_render_cache'] as $callable => $context) { + if (is_string($callable) && strpos($callable, '::') === FALSE) { + $callable = $this->controllerResolver->getControllerFromDefinition($callable); + } + $elements = call_user_func($callable, $elements, $context); + } + } + // If the default values for this element have not been loaded yet, populate // them. if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) { diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php index 216dfa8..c892150 100644 --- a/core/modules/block/src/BlockViewBuilder.php +++ b/core/modules/block/src/BlockViewBuilder.php @@ -36,33 +36,17 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N * {@inheritdoc} */ public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { + $class = get_class($this); + /** @var \Drupal\block\BlockInterface[] $entities */ $build = array(); - foreach ($entities as $entity) { + foreach ($entities as $entity) { + $entity_type = $entity->getEntityType()->id(); $entity_id = $entity->id(); $plugin = $entity->getPlugin(); - $plugin_id = $plugin->getPluginId(); $base_id = $plugin->getBaseId(); - $derivative_id = $plugin->getDerivativeId(); - $configuration = $plugin->getConfiguration(); - // Create the render array for the block as a whole. - // @see template_preprocess_block(). $build[$entity_id] = array( - '#theme' => 'block', - '#attributes' => array(), - // All blocks get a "Configure block" contextual link. - '#contextual_links' => array( - 'block' => array( - 'route_parameters' => array('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(), '#cache' => [ 'keys' => ['entity_view', 'block', $entity->id()], 'contexts' => $plugin->getCacheContexts(), @@ -73,19 +57,70 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la ), 'max-age' => $plugin->getCacheMaxAge(), ], - '#pre_render' => [ - [$this, 'buildBlock'], + '#pre_render_cache' => [ + $class . '::preRenderBlock' => compact('class', 'entity_type', 'entity_id', 'view_mode', 'langcode'), ], - // Add the entity so that it can be used in the #pre_render method. - '#block' => $entity, ); - $build[$entity_id]['#configuration']['label'] = SafeMarkup::checkPlain($configuration['label']); - // Don't run in ::buildBlock() to ensure cache keys can be altered. If an - // alter hook wants to modify the block contents, it can append another - // #pre_render hook. - $this->moduleHandler()->alter(array('block_view', "block_view_$base_id"), $build[$entity_id], $plugin); + $module_handler = $this->moduleHandler; + $build[$entity_id] = static::preRenderBlock($build[$entity_id], compact('class', 'entity', 'view_mode', 'langcode', 'module_handler')); + + // @todo Hack: Need an $entity->isDirty() function. + if ($base_id == 'system_main_block') { + unset($build[$entity_id]['#pre_render_cache']); + } } + + return $build; + } + + public static function preRenderBlock($build, $context) { + extract($context); + if (!isset($entity)) { + $entity = entity_load($entity_type, $entity_id); + } + if (!isset($module_handler)) { + $module_handler = \Drupal::service('moduleHandler'); + } + + $entity_id = $entity->id(); + $plugin = $entity->getPlugin(); + $plugin_id = $plugin->getPluginId(); + $base_id = $plugin->getBaseId(); + $derivative_id = $plugin->getDerivativeId(); + $configuration = $plugin->getConfiguration(); + + // Create the render array for the block as a whole. + // @see template_preprocess_block(). + $build = array( + '#theme' => 'block', + '#attributes' => array(), + // All blocks get a "Configure block" contextual link. + '#contextual_links' => array( + 'block' => array( + 'route_parameters' => array('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' => [ + $class . '::buildBlock', + ], + // Add the entity so that it can be used in the #pre_render method. + '#block' => $entity, + ) + $build; + + $build['#configuration']['label'] = SafeMarkup::checkPlain($configuration['label']); + + // Don't run in ::buildBlock() to ensure cache keys can be altered. If an + // alter hook wants to modify the block contents, it can append another + // #pre_render hook. + $module_handler->alter(array('block_view', "block_view_$base_id"), $build, $plugin); + return $build; } @@ -98,7 +133,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la * - if there is content, moves the contextual links from the block content to * the block itself. */ - public function buildBlock($build) { + public static function buildBlock($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.