Good morning, everyone,
I have just successfully completed the major upgrade from D10.3.16 to D11.3.16, preserving all previously configured features and customizations—except for one.
In my project, each node can potentially have several blocks (views of referenced nodes) linked via relationships or contextual filters. “Potentially” means that there may be nodes without any related bundles and therefore without any blocks.
This creates a layout issue: in these cases—and on the homepage—the sidebar remains empty, “breaking” the visual continuity of the layout itself.

The solution I found in D10.3.16 was to create a conditional block—let’s call it ‘cherry_test’: If there are other blocks in the sidebar, ‘cherry_test’ is rendered; otherwise, it remains hidden.  The prerequisite for this to work is that the block, in Layout Blocks, must be “disabled” (it must exist but not be active by default).

In D11.3.16, none of this works. The Block APIs have been modified—they’ve become more restrictive—and I can’t reproduce the same behavior in any way.

I’m sharing the ‘preprocess_region’ to better illustrate how the code works in the *.theme file

 

/**
 * Implements hook_preprocess_region(). Shows and hides the test block depending on whether the region (sidebar_second) is empty or occupied by other blocks (views).
 */

function cherry_preprocess_region(&$variables, $hook) {
  // 1. Make sure it’s the correct region.
  if ($variables[‘region’] === ‘sidebar_second’) {

    $is_empty = FALSE;

    // 2. SAFE DATA TYPE CHECK
    if (is_array($variables[‘content’])) {
      // It’s a render array: let’s render it as a string to see what it produces.
      // We use renderPlain() instead of render() to avoid conflicts with the
      // main page’s cache metadata during preprocessing.
      $renderer = \Drupal::service(‘renderer’);
      $output = (string) $renderer->renderPlain($variables[‘content’]);

      $allowed_media_tags = ‘<img><svg><video><iframe><canvas><picture>’;
      $clean_output = strip_tags($output, $allowed_media_tags);
      $is_empty = (trim($clean_output) === ‘’);
    }
    elseif ($variables[‘content’] instanceof \Drupal\Core\Render\Markup || is_string($variables[‘content’])) {
      // It is already a string or a Markup object: we check it directly.
      $allowed_media_tags = ‘<img><svg><video><iframe><canvas><picture>’;
      $clean_output = strip_tags((string) $variables[‘content’], $allowed_media_tags);
      $is_empty = (trim($clean_output) === ‘’);
    }
      else {
      // If it is neither an array nor a string, we consider it empty.
      $is_empty = TRUE;
    }

    // 3. If it is empty, we inject the fallback block.
    if ($is_empty) {
      $block_id = ‘cherry_test’;
      $block = \Drupal::entityTypeManager()->getStorage(‘block’)->load($block_id);

      if ($block) {
        // FORCE $variables[‘content’] to be an empty array before adding the block.
        // This prevents any future errors in the Renderer.
        $variables[‘content’] = [];

        $variables[‘content’][‘fallback_block_cherry_test’] = \Drupal::entityTypeManager()
          ->getViewBuilder(‘block’)
          ->view($block);

        // 4. Cache Management (with existence check for maximum security)
        if (!isset($variables[‘#cache’] [‘tags’])) {
          $variables[‘#cache’][‘tags’] = [];
        }
        $variables[‘#cache’][‘tags’] = array_merge(
          $variables[‘#cache’][‘tags’],
          $block->getCacheTags()
        );
      }
    }
  }
}

This feature is quite important given the nature of this project, and I’d like to figure out a way to implement it (we don’t necessarily have to follow the same approach to the letter—it, could very well be a different one—but the important thing is to replicate the same behavior with the “conditional block”).

Thank you in advance, and I’ll be very grateful to anyone who can help me find a solution, whether it’s similar or an alternative.

Comments

jaypan’s picture

A thought I have is to maybe render a block that simply renders a space character, or &nbsp;. Then I would place that block at the bottom of the sidebar, and give it no top or bottom margin. Finally, I would give the second last element (the previous sibling) no bottom margin. This way, the empty block would always render, acting as the bottom margin when other blocks are rendered, or an empty placeholder that forces rendering of the sidebar when no other blocks are present.


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.