I have a region with 4 blocks configured in. But from the theming POV I want at least the first three blocks seperated into its own container. So I have written the following code for a hook_preprocess_HOOK().
/**
* Implements hook_preprocess_HOOK()
*/
function hook_preprocess_region__footer(array &$variables) {
// TODO: After 'drush cr' this hook results in empty markup. After refreshing its rendered correctly.
// Get all valid elements as array of keys.
$children = Element::children($variables['elements']);
// Slice children into its first 3 and rest parts.
$childrenSliceFirst3 = array_slice($children, 0, 3);
$childrenSliceRest = array_slice($children, 3);
// Get the full render elements from the original elements key.
$renderElementsFirst3 = array_intersect_key($variables['elements'], array_flip($childrenSliceFirst3));
// Force the renderer to re-render all children.
foreach ($renderElementsFirst3 as &$value) {
$value['#printed'] = FALSE;
}
$renderElementsRest = array_intersect_key($variables['elements'], array_flip($childrenSliceRest));
foreach ($renderElementsRest as &$value) {
$value['#printed'] = FALSE;
}
// Create new render array for bulk rendering.
$footerMarkup['cols'] = [
'#theme_wrappers' => ['container' => ['#attributes' => ['class' => 'contact-cols']]],
] + $renderElementsFirst3;
$footerMarkup['rest'] = $renderElementsRest;
// Reset the original content markup with the new wrapped one.
$variables['content'] = \Drupal::service('renderer')->render($footerMarkup);
}
As we are in a preprocessing hook, the $variables['content']
is already rendered.
So I created a new render array with the new container as a #theme_wrappers
and re-rendered all.
Due to the early return in the Renderer service I only get an empty container without the blocks markup.
So I have to force the full recursive re-render by reset #printed = FALSE
on any child.
I think this is not performant and in this case completly unnecessary.
drush cr
// Drupal\Core\Render\Renderer at line 238.
// Change the original early return
// Do not print elements twice.
if (!empty($elements['#printed'])) {
return '';
}
// into
Do not print elements twice.
if (!empty($elements['#printed'])) {
return $elements['#markup'];
}
Fixing Test.
Changing the expected result from the second render call to $build['#markup']
And adding a child test with #printed = TRUE
option.
public function testRenderTwice($build) {
$this->assertEquals('kittens', $this->renderer->renderRoot($build));
$this->assertEquals('kittens', $build['#markup']);
$this->assertEquals(['kittens-147'], $build['#cache']['tags']);
$this->assertTrue($build['#printed']);
// We don't want to reprint already printed render arrays.
$this->assertEquals($build['#markup'], $this->renderer->renderRoot($build));
}
public function providerRenderTwice() {
return [
[
[
'child_printed' => [
'#markup' => 'kittens',
'#printed' => TRUE,
'#cache' => [
'tags' => ['kittens-147'],
],
],
],
],
];
}
With that change there is an error resulting in missing cache tags I cant find actually.
Maybe anyone can help here out.
Closed: outdated
9.5
Last updated
Not all content is available!
It's likely this issue predates Contrib.social: some issue and comment data are missing.