Drupal defining and theming custom blocks

Submitted by swim on Sat, 07/27/2013 - 04:11

Being able to provide custom markup through theming is important on a number of levels. It allows developers & themers alike the ability to implement their own structure with predefined variables which can often even differ on a block to block basis; some requiring complex tables or forms others a simple markup area.

This article will provide a quick tutorial on how to theme custom block content & how to implement your own markup in block theme files.

Note, this tutorial will require the creation of a custom module; if your unsure on how to do this please read this document first.

Before defining our custom block we'll create our theme hook.


/**
 * Implements hook_theme().
 */
function examplemodule_theme() {
  return array(
    'examplemodule_output' => array(
      'variables' => array(
        'title' => NULL,
        'content' => NULL,
        'popular_content' => NULL,
       ),
      'template' => 'templates/examplemodule-sweet--block',
    ),
  );
}

This tells Drupal our theme file examplemodule-sweet--block.tpl will live relatively to our module's location; so sites/all/modules/examplemodule/templates/examplemodule-sweet--block.tpl. Next we can define our custom block & the content callback.


/**
 * Implements hook_block_info().
 */
function examplemodule_block_info() {
  $blocks['sweet_output_block'] = array(
    'info' => t('Sweet Block'),
    'cache' => DRUPAL_CACHE_PER_ROLE,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function examplemodule_block_view($delta = '') {
  switch ($delta) {
    case 'sweet_output_block':
      $block['subject'] = t('Sweet Block Content');
      $block['content'] = _examplemodule_sweet_block_content();
    break;
  }
  return $block;
}

And lastly the custom block content.


/**
 * Callback implemented by hook_block_view().
 */
function _examplemodule_sweet_block_content() {
  $query = new EntityFieldQuery();
  $query->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'product_display')
    ->propertyCondition('status', 1)
    ->fieldCondition('field_display', 'value', 1, '=')
    ->range(0, 3)
    ->addMetaData('account', user_load(1));
  $result = $query->execute();

  if (isset($result['node'])) {
    $nids = array_keys($result['node']);
    $items = entity_load('node', $nids);

    $attributes = array(
      'id' => 'popular',
      'class' => 'popular-content',
    );
 
    foreach ($items as $item) {
      $popular_content[$item->nid] = array(
        'items' => array(
          'data' => l(t($item->title), 'node/' . $item->nid, array())
        ),
        'title' => NULL,
        'type' => 'ul',
        'attributes' => $attributes,
      );
    }
  }

  $static_title = t('Static Title');
  $static_content = 'static content';

  return theme('examplemodule_output', array(
      'title' => $static_title,
      'content' => $static_content,
      'popular_content' => theme('item_list', $popular_content),
    )
  );
}

Now our theme file will have access to the following variables title, content, & popular_content as returned in the function above. We'll ignore both title & content variables as they're simply for demo purposes, containing static strings. Popular content however will be an array and will need to be iterated over in examplemodule-sweet--block.tpl.php.

Note, standard block markup can be found here, block.tpl.php


foreach ($popular_content as $row) {
  print $row;
}