Integrating your custom module with MultiBlock in Drupal 7

Thu, 25/08/2016 - 13:55

One of the "late to the party" features of Drupal 8 is the ability to assign the same block to multiple locations in the theme. Drupal 7 still lacks this feature in the core, but it's easy to achieve it by using a contributed module such as MultiBlock.

While it works out of the box with all modules that provide blocks, you will need to modify your code in order to allow administrators to have separate configuration for each block instance. The official documentation for MultiBlock module is lacking and there is no straightforward example on what exactly you need to do in code. In this article I'll show how to integrate your custom module with MultiBlock and have different configuration for each instance of your block.

To continue, you can clone the Blank Module boilerplate. It will make it easier to create a new empty module and start coding immediately.

MultiBlock adds an extra page in the block admin section where you can create instances of existing blocks. Out of the box it works with all blocks but it does not allow you to have different configuration between instances. To achieve this, we must let MultiBlock know that our module can be integrated with it and add some extra code for fetching information related to the current instance.

Here's what we need to do in code:

  1. Implement hook_block_info(). To integrate with MultiBlock, we have to pass one extra key in the block array (mb_enabled) and set its value to TRUE.
    This will make MultiBlock instance ID available to us in hook_block_view() and hook_block_configure(), so we can use it for fetching configuration specific to the current instance.
  2. Implement hook_block_view(). MultiBlock will pass us instance ID only for blocks that integrate with MultiBlock.
  3. Implement hook_block_configure(). Just like above, we will get one extra argument with instance ID. Since the instance ID will not be provided by MultiBlock in the hook_block_save(), we will have to store it in the form as a hidden value.
  4. Implement hook_block_save(). Here we will just check if the instance ID is passed in the form, and store the settings under appropriate ID.

Below is full code. Do note that the $edit value provided by MultiBlock module will contain the value in the hidden field format (for whatever reason).

/**
 * Implements hook_block_info().
 */
function YOUR_MODULE_block_info() {
  $blocks = array();

  // This is the block name.
  $blocks['sample']['info'] = t('Sample Block');
  // We have to enable administrative interface for configuring our block.
  $blocks['sample']['properties']['administrative'] = TRUE;
  // Enable MultiBlock integration.
  $blocks['sample']['mb_enabled'] = TRUE;

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function YOUR_MODULE_block_view($delta = '', $edit = NULL) {
  $block = array();
  switch ($delta) {
    case 'sample':
      $block['subject'] = NULL;
      // Check if multiblock module is in use.
      $prefix = '';
      if ($edit && isset($edit['multiblock_delta']['#value'])) {
        $prefix = '_' . $edit['multiblock_delta']['#value'];
      }
      $block['content'] = variable_get('YOUR_MODULE_block_config' . $prefix . '_text');

      return $block;
  }
}

/**
 * Implements hook_block_configure().
 */
function YOUR_MODULE_block_configure($delta = '', $edit = NULL) {
  $form = array();

  switch ($delta) {
    case 'sample':
      // If MultiBlock is installed, pass the delta as a hidden value so we can
      // retrieve the correct variable from the database in hook_block_save()
      // implementation.
      $prefix = '';
      if ($edit && isset($edit['multiblock_delta'])) {
        $form['multiblock_delta'] = array(
          '#type' => 'value',
          '#value' => $edit['multiblock_delta']['#value'],
        );
        $prefix = '_' . $edit['multiblock_delta']['#value'];
      }
      $form['sample'] = array(
        '#type' => 'fieldset',
        '#title' => t('Block configuration'),
      );
      $form['sample']['text'] = array(
        '#type' => 'textfield',
        '#title' => t('Enter some text'),
        '#required' => TRUE,
        '#default_value' => variable_get('YOUR_MODULE_block_config' . $prefix . '_text'),
      );

      return $form;
  }
}

/**
 * Implements hook_block_save().
 */
function YOUR_MODULE_block_save($delta = '', $edit = array()) {
  switch ($delta) {
    case 'sample':
      $prefix = ($edit['module'] == 'multiblock') ? '_' . $edit['delta'] : '';
      variable_set('YOUR_MODULE_block_config' . $prefix . '_text', check_plain($edit['text']));
      break;
  }
}

After enabling your custom module, you can go to Structure -> Blocks -> Instances and create as many instances of your block as you need. Each one of them can have different text.

Bonus points: do remember to delete your variables when your module is uninstalled from the system. To do that, the following code would go to your .install file:

/**
 * Implements hook_uninstall().
 */
function YOUR_MODULE_uninstall() {
  // This is easiest approach because it will remove all variables at once.
  db_query("DELETE FROM {variable} WHERE name LIKE :variables", array(':variables' => 'YOUR_MODULE_%'));
}

See also: