Score:1

Drupal 8/9 Layout Builder and default custom block

fr flag

We have a number of custom blocks that we've created, and use Layout Builder so that our authors can just drag and drop as needed. We have one Hero block in particular that we would like to see on every page (customized per page), and would love to automatically insert one on node creation for ease of authoring. You can almost do this via default layouts for the content type, but the default block acts more like a reusable block. The first page gets the empty block, user edits it and saves. Any subsequent page then get that edited version.

I'm running the following in a hook_preprocess_HOOK (hook_preprocess_page specifically):

$layoutBuilder = $node->get('layout_builder__layout');
$sections = $layoutBuilder->getSections();
if (isset($sections) && !empty($sections)) {
  $hasHero = FALSE;
  $heroSection = $sections[0];
  $components = $heroSection->getComponents();
  foreach ($components as $component) {
    $blockPlugin = $component->getPlugin();
    if ($blockPlugin instanceof BlockBase) {
      $blockConfig = $blockPlugin->getConfiguration();
      if ($blockConfig['id'] === 'hero_cta') {
        $hasHero = TRUE;
        break;
      }
    }
  }
  if (!$hasHero) {
    $blockEntityManager = \Drupal::entityTypeManager()
      ->getStorage('block_content');

    $block = $blockEntityManager->create(
      [
        'info' => 'Hero CTA for node/' . $nid,
        'type' => 'hero_cta',
        'langcode' => 'en',
      ]
    );
    $block->save();

    $newBlockConfig = [
      'id' => 'hero_cta',
      'provider' => 'hero_cta',
      'label_display' => TRUE,
      'block_id' => $block->id(),
      'context_mapping' => [],
    ];
    $newComponent = new SectionComponent($node->uuid(), 'content', $newBlockConfig);
    $heroSection->appendComponent($newComponent);

    $node->save();
  }
}

The above almost works... When I go to the View tab, I can see it. But, when I go to the layout tab, it is not there. If you save the layout override, and then refresh again, it suddenly appears.

So, what am I doing wrong?

apaderno avatar
us flag
In which `hook_preprocess_HOOK()` is that code exactly used? `hook_preprocess_HOOK()` is probably the wrong place to save a block, though.
fr flag
Ooop, yeah, edited question with that additional info @apaderno - currently using `hook_preprocess_page`. It seemed like that was the next best place to try and perform this task after the content type is saved the first time. The block is indeed being created, but the connection of it to the section component is where it is kinda failing for me. So yes, appropriate function timing could be my issue.
Score:0
fr flag

Alright, our team eventually found the solution we were after, so posting here in case someone else is trying to do this! Ultimately, as pointed out, I was initially doing this in a preprocess hook - which was then being overwritten by the layout defaults. Here is what worked:

protected function prepareLayout(SectionStorageInterface $section_storage) {
  parent::prepareLayout($section_storage);
  $storageContext = $section_storage->getContexts();
  $node = $storageContext['entity']->getContextData()->getValue();
  if (isset($node)) {
    if ($node->hasField(OverridesSectionStorage::FIELD_NAME)) {
      $nid = $node->id();
      $layoutBuilder = $node->get(OverridesSectionStorage::FIELD_NAME);
      $sections = $layoutBuilder->getSections();
      if (isset($sections) && !empty($sections)) {
        // Set default configuration to compare to make sure page has been generated.
        $defaultHeroSection = $section_storage->getDefaultSectionStorage()->getSection(0);
        $defaultHeroCount = count($defaultHeroSection->getComponents());

        // First section is our 'Hero CTA' region. Check for existence.
        $hasHero = FALSE;
        $heroSection = $layoutBuilder->getSection(0);
        $heroComponents = $heroSection->getComponents();
        $currentHeroCount = count($heroComponents);
        if ($defaultHeroCount === $currentHeroCount) {
          foreach ($heroComponents as $component) {
            $blockPlugin = $component->getPlugin();
            if ($blockPlugin instanceof BlockBase) {
              $blockConfig = $blockPlugin->getConfiguration();
              if ($blockConfig['id'] === 'hero_cta') {
                $hasHero = TRUE;
                break;
              }
            }
          }
          if (!$hasHero) {
            $blockEntityManager = \Drupal::entityTypeManager()->getStorage('block_content');

            // Create a new, empty Hero CTA block.
            $block = $blockEntityManager->create(
              [
                'info' => 'Hero CTA for node/' . $nid,
                'type' => 'hero_cta',
                'langcode' => 'en',
              ]
            );
            $block->save();

            // Insert new Hero CTA block into proper section.
            $newBlockConfig = [
              'id' => 'hero_cta',
              'provider' => 'hero_cta',
              'label_display' => TRUE,
              'block_id' => $block->id(),
              'context_mapping' => [],
            ];
            $newComponent = new SectionComponent($node->uuid(), 'content', $newBlockConfig);
            $heroSection->appendComponent($newComponent);
          }
          // Remove original first section and unused blocks.
          $section_storage->removeSection(0);
          // Reinsert first section.
          $section_storage->insertSection(0, $heroSection);
          $this->layoutTempstoreRepository->set($section_storage);
          // Save the changes.
          $node->save(); 
        }
      }
    }
  }
}
fr flag
And, should someone come across this thread and need a D9 solution, you'll need to move this functionality into an event subscriber.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.