Re-rendering render array with already #printed childs results in empty #markup

Created on 27 September 2022, about 2 years ago
Updated 14 July 2023, over 1 year ago

Problem/Motivation

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.

Steps to reproduce

  • Add some blocks into any region
  • Add the posted hook_preprocess_HOOK into your theme
  • drush cr
  • Result: Empty region
  • Refresh Browser with F5
  • Result: correctly rendered region

Proposed resolution

// 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'];
}

Remaining tasks

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.

User interface changes

API changes

Data model changes

Release notes snippet

πŸ’¬ Support request
Status

Closed: outdated

Version

9.5

Component
RenderΒ  β†’

Last updated about 8 hours ago

Created by

πŸ‡©πŸ‡ͺGermany sunlix Wesel

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.71.5 2024