Skip to main content

Harness the power of Display Suite's API to create custom display fields

Drupal display suite custom fields
Too Long?
See the abridged version

In an effort to keep control over how nodes are displayed in Drupal's 'Manage display' section and avoid unnecessary template files, the idea of creating special 'display fields' has been floating around for a while now.

You can do this with straight-up Drupal fairly easily (though in a limited way) using hook_field_extra_fields() but if you are going to be using the fantastic Display Suite module anyway there is an even better way.

On top of the custom fields you can add using Display Suite's UI, the Display Suite API has its own nifty way to create custom fields in code. When added this way, you can even give each display field its own settings, allowing for configuration that isn’t possible with either hook_field_preprocess_field() or any of Display Suite's other methods for adding custom fields. This can make your custom display fields a lot more flexible and reusable.

The Display Suite module provides an excellent, flexible way to achieve this using it's API.

For the sake of example, I'm going create a really simple custom display field that displays the Human Readable name of the content type. I will also be adding a setting to the field that will override the name if for some reason (who knows) we want that.

So first lets simply create the field and get it displaying something - we can add the settings afterwards.

/**
 * Implements hook_ds_fields_info().
 */
function MODULE_ds_fields_info($entity_type) {
  $fields = array();

  $fields['content_type_name_display'] = array(
    'title' => t('Content type name'),
    'field_type' => DS_FIELD_TYPE_FUNCTION,
    'function' => 'MODULE_custom_field_render',
    'ui_limit' => array('*|teaser'), // Optional but cool - limit this field to just teasers for example.
  );

  return array('node' => $fields);
}​

/**
 * Custom function to generate the content for any custom fields created above
 * based on field name.
 */
function MODULE_custom_field_render($field) {
  switch ($field['field_name']) {
    case 'content_type_name_display':
      // Get the human readable content type name.
      $type = node_type_load($field['entity']->type);
      $content_type = $type->name;  

      $field_content = t($content_type);
      break;

  }

  return $field_content;
}

So that's pretty simple really, first using hook_ds_fields_info() we declare any custom fields, and then using a custom function we can give them content, in this case getting the content type name. But what if we wanted to be able to override the content name sometimes because... I dunno why, it's just an example but let's say we do - the good news is we can easily add a setting to our field for this.

So first we need to add a couple of things to our hook_ds_fields_info()

/**
 * Implements hook_ds_fields_info().
 */
function MODULE_ds_fields_info($entity_type) {
  $fields = array();

  // Content type name display with override setting if needed.
  $fields['content_type_name_display'] = array(
    'title' => t('Content type name'),
    'field_type' => DS_FIELD_TYPE_FUNCTION,
    'function' => 'MODULE_custom_field_render',
    'properties' => array(
      'settings' => array(
        'content_type_name_override' => array(
          'type' => 'textfield',
          'description' => t('Override the default provided by Drupal. Leave empty to use the default.'),
        ),
      ),
      'default' => array(
        'content_type_name_override' => '',
      ),
    ),
  );

  return array('node' => $fields);
}

So you can see that we have added all that stuff inside the properties array to give us a setting called content_type_name_overide. That's still not quite enough to get it to show up though, now that we have settings we now need to add two new hooks - hook_ds_field_settings_form() and hook_ds_field_format_summary():

/**
 * Implements hook_ds_field_settings_form().
 */
function MODULE_ds_field_settings_form($field) {
  return ds_ds_field_settings_form($field);
}

/**
 * Implements hook_ds_field_format_summary().
 */
function MODULE_ds_field_format_summary($field) {
  $summary = ds_ds_field_format_summary($field);
  $summary = ($summary != '') ? $summary : 'Use default content type name';
  return $summary;
}

Pretty small, simple hooks though, nothing too lavish and this setting should now show up in the settings for our field:

/**
 * Implements hook_ds_fields_info().
 */
function MODULE_ds_fields_info($entity_type) {
  $fields = array();

  // Content type name display with override setting if needed.
  $fields['content_type_name_display'] = array(
    'title' => t('Content type name'),
    'field_type' => DS_FIELD_TYPE_FUNCTION,
    'function' => 'MODULE_custom_field_render',
    'ui_limit' => array('*|teaser'), // Optional but cool - limit this field to just teasers for example.
    'properties' => array(
      'settings' => array(
        'content_type_name_override' => array(
          'type' => 'textfield',
          'description' => t('Override the default provided by Drupal. Leave empty to use the default.'),
        ),
      ),
      'default' => array(
        'content_type_name_override' => '',
      ),
    ),
  );

  return array('node' => $fields);
}

/**
 * Implements hook_ds_field_settings_form().
 *
 * Required hook for content_type_name_display to display settings form.
 */
function MODULE_ds_field_settings_form($field) {
  return ds_ds_field_settings_form($field);
}

/**
 * Implements hook_ds_field_format_summary().
 *
 * Required hook for content_type_name_display to display settings form summary.
 */
function MODULE_ds_field_format_summary($field) {
  $summary = ds_ds_field_format_summary($field);
  $summary = ($summary != '') ? $summary : 'Use default content type name';
  return $summary;
}

/**
 * Generate custom field content.
 *
 * Custom function to generate the content for any custom fields created above
 * based on field name.
 */
function MODULE_custom_field_render($field) {
  switch ($field['field_name']) {
    case 'content_type_name_display':
      // If it has been overridden in the settings use that value instead.
      if (isset($field['formatter_settings']) && isset($field['formatter_settings']['content_type_name_override'])) {
        $content_type = $field['formatter_settings']['content_type_name_override'];
      }
      else {
        // Get the human readable content type name.
        $type = node_type_load($field['entity']->type);
        $content_type = $type->name;
      }
      $field_content = t($content_type);
      break;

  }
  return $field_content;
}

So we can now have a custom display field with a simple setting we can select:

So all that is left to do is amend our custom function that returns the field content so that it acknowledges this setting:

/**
 * Custom function to generate the content for any custom fields created above
 * based on field name.
 */
function MODULE_custom_field_render($field) {
  switch ($field['field_name']) {
    case 'content_type_name_display':
      // If it has been overridden in the settings use that value instead.
      if (isset($field['formatter_settings']) && isset($field['formatter_settings']['content_type_name_override'])) {
        $content_type = $field['formatter_settings']['content_type_name_override'];
      }
      else {
        // Get the human readable content type name.
        $type = node_type_load($field['entity']->type);
        $content_type = $type->name;
      }
      $field_content = t($content_type);
      break;

  }

  return $field_content;
}

Simple as that, this is a really basic example but the possibilities are surely endless!