Back

Create Basic Blocks Content programmatically in Drupal 8

Currently, our Drupal team is at work on the second stage of a Drupal corporate website for a US-based energy company. The site is in production and the client’s content management team is working on content population. However, they still need some additional functionality, as well as new types of content pages and other matters.

Using a case study, I will describe below how we overcame some of the challenges for this project.

A set of pages on the site was built on a previous step. These pages have specific path patterns:

markets/[category name]/[subcategory name]

There should be four special blocks with reference materials that will be shown in the sidebar of each category page and its subcategories. That is, the first block will be shown on all pages with path ‘markets/Category1*’ and the second block will be shown on ‘markets/Category2*’, etc.

Because there are no special requirements for those blocks, we decided to use native Drupal blocks (the basic block type) in the interest of saving time.

I have set all these blocks locally, but how can I put them on production without manual work?

I used Features module and select all necessary blocks in the Features UI. IN the module that I used the Features module and selected all the necessary blocks in the Features UI. In the module that was generated, there were several files with block settings in the folder /config/install/. (By the way, we can export these configurations with the Config manager, and the result will be the same.)

Let me describe the issue and our solution on the basis of one such block. For example, there is a basic block called “Communications.” Its configuration will be exported in the file ‘block.block.communications.yml’:

langcode: en
status: true
dependencies:
  content:
    - 'block_content:basic:4eac474a-b60f-4abf-aa2d-5bc8f71b4ad9'
  module:
    - block_content
    - system
  theme:
    - custom_theme
id: communications
theme: custom_theme
region: sidebar
weight: -14
provider: null
plugin: 'block_content:4eac474a-b60f-4abf-aa2d-5bc8f71b4ad9'
settings:
  id: 'block_content:4eac474a-b60f-4abf-aa2d-5bc8f71b4ad9'
  label: 'Communications'
  provider: block_content
  label_display: '0'
  status: true
  info: ''
  view_mode: full
visibility:
  request_path:
    id: request_path
    pages: '/markets/communications*'
    negate: false
    context_mapping: {  }

There is no content in this block! What’s more, if you put this configuration on production site you will get the following message in the sidebar instead of the block content:

This block is broken or missing. You may be missing content or you might need to enable the original module.

I did not find a quick solution to this problem with the help of Drupal contributed modules. The default content module looks like a rather complex solution that serves only to deploy a few basic blocks. If this kind of issue continues to arise, you might add a  Drupal Deploy module. However, I am not sure that it works with block content; I have used it for node content only.

On my own, I have come up with a simpler way to put block content on production. This can be done programmatically. We can create a [your feature name].install file in the module generated by Features UI. Or we can create a separate module that will contain two files: ([your module name].info.yml and [your module name].install).

For the previous module that used as a simple example, the [your module name].install file will look like this:

<?php

use Drupal\block\Entity\Block;

/**
 * Implements hook_install().
 */
function your_module_install() {
    // Grab a block entity manager from EntityManager service
    $blockEntityManager = \Drupal::service('entity.manager')
      ->getStorage('block_content');
    
    // Tell block entity manager to create a block of type 'basic'
    $block = $blockEntityManager->create(array(
      'type' => 'basic'
    ));
    
    // Every block should have a description, but strangely it's property
    // is not 'description' but 'info'
    $block->info = 'Communications';
    
    // we should take this UUID from configuration file, see example above
    $block->uuid = '4eac474a-b60f-4abf-aa2d-5bc8f71b4ad9'; 

    // Block content 
    $block->body->value = '...text of the block with allowed HTML tags...';
    
    // Block Input Format, because block content formated long text
    $block->body->format = 'full_html';

    // In the end, save our new block.
    $block->save(); 
}

One thing to keep in mind is that you should set the same UUID as in your configuration file. This will create a connection between block configuration and block content.

  • Manuel Adan

    To solve this, I just published the Fixed block content module ( https://www.drupal.org/project/fixed_block_content ). The module provides a type of block based on configuration (the fixed block) that wraps a custom block (the content block). If the content block does not exist, it is created automatically with a default content or empty if no default content was set. Fixed blocks are always present, so no “This block is broken or missing.” messages to the users. Everything is done through UI, no special files or custom module are needed.