Drupal: Example Custom Field Formatter

Scenario:

From the wiki page Energy Performance Certificate :

Energy Performance Certificates (EPCs) were introduced in England and Wales on 1 August 2007 as part of Home Information Packs (HIPs) for domestic properties with four or more bedrooms. Over time this requirement was extended to smaller properties. When the requirement for HIPs was removed in May 2010, the requirement for EPCs continued. The scheme for HIPs was extended to encompass three bedroom homes from 10 September 2007. Rental properties, which have a certificate valid for 10 years, required on a new tenancy commencing on or after 1 October 2008.They are a result of European Union Directive 2002/91/EC relating to the energy performance of buildings, as transposed into British law by the Housing Act 2004 and The Energy Performance of Buildings (Certificates and Inspections) (England and Wales) Regulations 2007 (S.I. 2007/991).

Problem:

You have a drupal node in which you want to store the energy value for a domestic property.  This is a number between 1 and 100.  However, when displaying this value you want to format it to include an image similar to the ones shown in the above wiki.

Solution:

Define a custom format type.

In this example we will wrap the energy value field and its custom formatter in a new node type Energy Class.

Step1 :

Create a new node type called Energy Class.

Step2:

Then creates a new field and field instance called energy_value.
Attached this field to the new node ( 'bundle' => 'energy' ).
This is all done in an install file using hook_install() and hook_uninstall() as shown below:


/**
 * Implements hook_install().
 */
function energy_install()
{
    // Create the Energy Class type.
    $t = get_t();

    $name = 'energy';

    $info = array(
        'type' => $name,
        'name' => $t('Energy Class'),
        'base' => 'node_content',
        'title_label' => $t('Energy Class Title'),
        'description' => $t('Basic node to hold energy value and display corresponding rating diagram.'),
        'custom' => TRUE,
    );

    $content_type = node_type_set_defaults($info);

    node_type_save($content_type);

    // Turn off comments.
    variable_set('comment_energy', '1');

    _energy_value_field_create();
}

function _energy_value_field_create()
{
    // Create the field if needed.
    if (!field_read_field('energy_value', array('include_inactive' => TRUE))) {
        $field = array(
            'field_name' => 'energy_value',
            'cardinality' => 1,
            'type' => 'number_integer',
        );
        field_create_field($field);
        $instance = array(
            'field_name' => 'energy_value',
            'label' => 'Energy Value',
            'entity_type' => 'node',
            'bundle' => 'energy',
            'required' => TRUE,
            'display' => array(
                'default' => array(
                    'type' => 'energy_value_formatter',
                    'label' => 'hidden',
                ),
            ),
            'widget' => array(
                'label' => 'inline',
            ),
        );
        field_create_instance($instance);
    }
}


/**
 * Implements hook_uninstall().
 */
function energy_uninstall()
{
    field_delete_field('energy_value');
    field_purge_batch(30);
    node_type_delete('energy');
}

Points to note:

  • Field name = energy_value
  • Type = number_integer
  • Display Type = energy_value_formatter (the custom formatter to be described below)
  • There are many ways that we could have 'hooked' in to add the field.  We chose to wrap it in hook_install().  An alternative could have been hook_node_type_insert().

Step 4:

At this point we have just created a new content type with a single Integer field.  Next we want to define how this number gets displayed.
First thing to do is let Drupal know about the new formatter and that it will apply to field types of number_integer.  This is achieved by implementing hook_field_formatter_info():


/**
 * Implements hook_field_formatter_info().
 */
function energy_field_formatter_info() {
  return array(
    'energy_value_formatter' => array(
      'label' => t('Energy Value Formatter'),
      'field types' => array('number_integer'),
    ),
  );
}

We can then override the display for the energy_value by implementing hook_field_formatter_view():


/**
 * Implements hook_field_formatter_view().
 *
 */
function energy_field_formatter_view($object_type, $object, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'energy_value_formatter':
      foreach ($items as $delta => $item) {
        $element[$delta]['#type'] = 'markup';
        $energyValue = $item['value'];
        $element[$delta]['#markup'] = theme('energy_value', array('energyValue' => $energyValue));
      }
      break;
  }
  return $element;
}

Here we intercept the display type matching our new formatter and fill in the $element return type with details of a theme function to use to generate the output we require:


/**
 * Implements hook_theme().
 *
 * This lets us tell Drupal about our theme functions and their arguments.
 */
function energy_theme($existing, $type, $theme, $path) {
  return array(
    'energy_value' => array(
      'variables' =>array('energyValue' => NULL),
    ),
  );
}

/**
 * A custom theme function.
 *
 * By using this function to format our node-specific information, themes
 * can override this presentation if they wish.
 */
function theme_energy_value($variables) {
  $energyValue = $variables['energyValue'];
  $energyBand = _getEnergyBand($energyValue);
  $margin = _getMarginOffset($energyBand);
  $path = drupal_get_path('module', 'energy');
  $imagePath =  preg_replace('/\/\?q=/', '', url($path)) . '/images/energy_efficiency_diagram.png';

  drupal_add_css($path . '/css/energy.css', array('group' => CSS_DEFAULT, 'every_page' => FALSE));
  $output = '<div class="gg_energy_label">Energy Efficiency Rating : ' . $energyValue . '</div>';
  $output .= ' <div class="gg_energy_box">';
  $output .= '    <div class="gg_energy_img"><img src="' . $imagePath . '" border="0" alt="EPC" title="EPC"/></div>';
  $output .= '   <div class="gg_energy_class">';
  $output .= '<div style="margin-top:' . $margin . 'px;" class="gg_energy_class_text"><span>Class ' . $energyBand .'</span></div>';
  $output .= '</div>';
  $output .= '<div class="gg_clearboth"></div>';
  $output .= '</div>';

  return $output;
}

The hook energy_field_formatter_info() defines the formatter for our energy value.  The function returns an array keyed on the formatter name.  In this case, energy_value_formatter.  Like everthing in drupal, the information for this formatter is attached as a child array of the main array.  The documentation defines what information can be appended :

  • label: The human-readable name of the formatter type.
  • ​​​​​​description: A short description for the formatter type.
  • field types: An array of field types the formatter supports.
  • settings: An array whose keys are the names of the settings available for the formatter type, and whose values are the default values for those settings.

 

 

 

Screenshots: