In my personal blog I wanted to insert a block directly into a node's body field. The block contains an image and I want text to wrap around the image, therefore the block needs to be in the same div as the rest of the content. I could have hacked the tpl.php file and done something like an array_splice but I wanted something a little more elegant and Drupalish. A custom field formatter is exactly what I need, read more to see how I did it.

In Drupal, a custom field formatter allows you to customize how field data are displayed depending on the view mode (eg. click the Manage Display tab when editing a content type). For example, a node from a Content Type may have a different display depending if content is displayed in a teaser, a full-page view, or some other view of your choosing. In my case, I only want the block to appear on the full-page node view.

My first step is to create the field formatter using hook_field_formatter_info():

/**
 * Implements hook_field_formatter_info().
 */
function rjt_blog_field_formatter_info() {
  $custom_formatters['rjt_blog_body_block'] = array(
    // Set the label that appears in the drop-down selector on the Manage Display page
    'label' => t('Adsense in body'),
    // This formatter will only be available for text fields with a "text with summary" widget
    'field types' => array('text_with_summary'),
    'description' => t('This formatter inserts an Adsense image block into the main body content'),
  );
  return $custom_formatters;
}

Now when I go to the manage display screen for the content type, I see that formatter available:

Manage Display

Next I'm going to build a renderable array for the field value in which I'm interested. I'm using this to create and pass variables to hook_theme, which I'll create in the next step. See my comments inline:

/**
 * Implements hook_field_formatter_view().
 *
 * $entity_type     The type of $entity.
 * $entity          The entity being displayed.
 * $field           The field structure.
 * $instance        The field instance.
 * $langcode        The language associated with $items.
 * $items           Array of values for this field.
 * $display         The display settings to use, as found in the "display" entry of instance definitions.
 *                  The array notably contains the following keys and values;
 * type:            The name of the formatter to use.
 * settings:        The array of formatter settings.
 */
function rjt_blog_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'];
  switch ($display['type']) {
    // This is the "field type" that I set in hook_field_formatter_info()
    case 'rjt_blog_body_block':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          // I'll use these values in hook_theme(), which I'll do in the next function
          '#theme'  => 'rjt_blog_body',
          // Have the devel module enabled and use dsm($entity) to see available field values.
          // I can pass these values to hook_theme, which I'll do in the next function.
          // In this case, I'm passing #body to hook_theme() and the actual value comes from the $entity.
          '#body' => $entity->body['und'][0]['safe_value'],
        );
      }
      break;
  }
  return $element;
}

Now I need to use hook_theme to create a tpl.php so I can have free reign over the HTML output in the body field. Here goes:

/**
* Implements hook_theme().
*/
function rjt_blog_theme() {
  $items = array();
 
  $items['rjt_blog_body'] = array(
    // The template will be in the module's templates directory and will be named
    // rjt-blog-body.tpl.php.
    'template'  => 'templates/rjt-blog-body',
    'variables' => array(
      // This value comes from hook_field_formatter_view() above
      'body' => '',
    )
  );
 
  return $items;
}

Finally I get to actually insert the block into the body field. First I create the file templates/rjt-blog-body.tpl.php, which contains the following:

<div id="right-rail-skyscraper">
  <?php    
    $block = module_invoke('adsense_managed', 'block_view', '0');
    print $block['content'];
  ?>
</div>
<?php print $body; ?>

Then I clear cache. Now if I go to view a full-page node view, I see that content from the body field is properly wrapping around an image from a block:

Using a custom field formatter to insert a block in content in Drupal

Done!

Comments

Fieldblock is good if you're working with fields from a node. In my use-case I had a block from another module (Google Adsense) that I wanted inserted directly into the body field. I don't think Fieldblock does this, but it's been awhile since I used that module...

Add new comment

The content of this field is kept private and will not be shown publicly.