7 field.api.php hook_field_extra_fields()

Exposes "pseudo-field" components on fieldable entities.

Field UI's "Manage fields" and "Manage display" pages let users re-order fields, but also non-field components. For nodes, these include the title, poll choices, and other elements exposed by modules through hook_form() or hook_form_alter().

Fieldable entities or modules that want to have their components supported should expose them using this hook. The user-defined settings (weight, visible) are automatically applied on rendered forms and displayed entities in a #pre_render callback added by field_attach_form() and field_attach_view().

Return value

A nested array of 'pseudo-field' elements. Each list is nested within the following keys: entity type, bundle name, context (either 'form' or 'display'). The keys are the name of the elements as appearing in the renderable array (either the entity form or the displayed entity). The value is an associative array:

  • label: The human readable name of the element.
  • description: A short description of the element contents.
  • weight: The default weight of the element.
  • edit: (optional) String containing markup (normally a link) used as the element's 'edit' operation in the administration interface. Only for 'form' context.
  • delete: (optional) String containing markup (normally a link) used as the element's 'delete' operation in the administration interface. Only for 'form' context.

See also

_field_extra_fields_pre_render()

hook_field_extra_fields_alter()

Related topics

5 functions implement hook_field_extra_fields()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

comment_field_extra_fields in modules/comment/comment.module
Implements hook_field_extra_fields().
node_field_extra_fields in modules/node/node.module
Implements hook_field_extra_fields().
poll_field_extra_fields in modules/poll/poll.module
Implements hook_field_extra_fields().
taxonomy_field_extra_fields in modules/taxonomy/taxonomy.module
Implements hook_field_extra_fields().
user_field_extra_fields in modules/user/user.module
Implements hook_field_extra_fields().
1 invocation of hook_field_extra_fields()
FieldInfo::getBundleExtraFields in modules/field/field.info.class.inc
Retrieves the "extra fields" for a bundle.

File

modules/field/field.api.php, line 47
Hooks provided by the Field module.

Code

function hook_field_extra_fields() {
  $extra['node']['poll'] = array(
    'form' => array(
      'choice_wrapper' => array(
        'label' => t('Poll choices'),
        'description' => t('Poll choices'),
        'weight' => -4,
      ),
      'settings' => array(
        'label' => t('Poll settings'),
        'description' => t('Poll module settings'),
        'weight' => -3,
      ),
    ),
    'display' => array(
      'poll_view_voting' => array(
        'label' => t('Poll vote'),
        'description' => t('Poll vote'),
        'weight' => 0,
      ),
      'poll_view_results' => array(
        'label' => t('Poll results'),
        'description' => t('Poll results'),
        'weight' => 0,
      ),
    )
  );

  return $extra;
}

Comments

The documentation above says "... elements exposed by modules through hook_form() or hook_form_alter()." but why does it limit itself to forms when it also works with hook_node_view et al?

Seems to be some issues with adding pseudo-fields for the $form['options'['somefield'] type setup, see http://drupal.org/node/1488956

The following exposes account picture as a field in display management pages.

<?php
/**
 * Implements hook_field_extra_fields().
 */
function yourmodule_field_extra_fields() {
 
$extra['user']['user'] = array(
   
'display' => array(
     
'picture' => array(
       
'label' => t('Picture'),
       
'description' => t('Picture'),
       
'weight' => 0,
      ),
    )
  );
  return
$extra;
}
?>

The same can be achieved in drupal 6 with

/**
* Implementation of hook_content_extra_fields.
*/
function MODULE_NAME_content_extra_fields() {
  $extras['custom_field'] = array(
    'label' => t('Custom field'),
    'description' => t('Custom field to be displayed in UI.'),
    'weight' => 100,
  );
  return $extras;
}

Note that if you implement this hook, you'll probably also want this patch:
https://drupal.org/node/1256368
to prevent your new field from suddenly being enabled everywhere.

I've found a very helpful function for displaying the "pseudo-field". I'm using it in hook_entity_view() instead of hook_node_view(), but should be the same

<?php
function my_module_entity_view($entity, $type, $view_mode, $langcode) {
  // Extra fields for nodes
  if ($type == 'node' && $entity->type == 'poll') {
    $extra_fields = field_extra_fields_get_display('node', $entity->type, $view_mode);

    // Get extrafield visibility.
    if (!empty($extra_fields['poll_view_voting']) && $extra_fields['poll_view_voting']['visible'] == TRUE) {
     
      // Do something awesome depending on the $view_mode (I've just added a default for all view_modes)
      switch ($view_mode) {
        default:
          $entity->content['poll_view_voting'] = array(
            '#markup' => '<div class="my-class">Foo poll content</div>'
          );
      }
    }
  }
}

Note that if you define the default class in 'extra fields controller class' key when defining your entity, EntityDefaultExtraFieldsController include all of your properties in the Manage display tab. So if you also return the 'display' key with this hook you will get a warning message when entering to the Manage display tab:

Warning: htmlspecialchars() expects parameter 1 to be string, array given in htmlspecialchars()

In this case you only need to return the 'form' part of the array within this hook to get your properties visible at the Manage fields tab.

I'm using the following, whereby I implement the callback function option when specifying the field. The callback function is then used to set the field value. The benefit is that the field will also be available in views, if you use extrafield_views_integration module.

<?php
/**
* Implements hook_field_extra_fields().
*/
function my_module_field_extra_fields() {
  $extra = array();
    // Define extra invoice fields
    $extra['node']['invoice'] = array(
      'display' => array(
        // Customer ID field
        'field_i_total' => array(
          'label' => t('Total'),
          'description' => t('Invoice Total.'),
          'weight' => 0,
          'callback' => '_extra_field_i_total_callback'
        )
      ),
    );
  return $extra;
}

/**
* Callback function.
* Returns value for extra field: field_i_total.
*/
function _extra_field_i_total_callback($entity) {
  // a bunch of logic
  $total = '5000';
  return $total;
}

/**
* Implements hook_entity_view().
*/
function my_module_entity_view($entity, $type, $view_mode, $langcode) {
  // Extra fields
  $extra_fields = field_info_extra_fields($type, $entity->type, 'display');
  // Check the field is displayed for the current view_mode
  $field_name = 'field_i_total';
  if(!empty($extra_fields[$field_name]['display'][$view_mode]['visible'])) {
    $entity->content[$field_name] = array(
      '#markup' => _extra_field_i_total_callback($entity)
    );
  }
}

Is [field_i_total][callback] doing anything tho? Because you're calling the function yourself in hook_entity_view().

It is required for integration with Views using this module https://www.drupal.org/project/extrafield_views_integration

Would you also happen to know how to implement a settings form for an extra field. The callback function is kinda useless to me without having the ability to set parameters for the extra field.

Hi alexverb.

Maybe a solution :
First, you can't use the same logic for extra-fields and real fields. Real fields use hook_formatter_* implementations, but extra-fields are not fields defined in field API. You can implements hook_form_field_ui_display_overview_form_alter (hook_form_FORM_ID_alter) to alter display form and use the "settings_edit" form key to add extra configuration.

A sample here :

<?php
/**
 * Implements hook_field_extra_fields to expose
 * wfivestar data to forms and displays
 */
function wfivestar_field_extra_fields() {
 
$extra = array();

 
// Get node entity info
 
$entity_info = entity_get_info('node');

 
// Iterate on each bundle
 
foreach (array_keys($entity_info['bundles']) as $bundle) {

   
// Test if wfivestar is enabled for current bundle for form
   
$form_values = variable_get('wfivestar_'.$bundle.'_form', array('state' => FALSE));
    if (
$form_values['state']) {

     
// Add a form component, to expose new "field" into
      // "manage fields" bundle administration
     
$extra['node'][$bundle]['form']['wfivestar'] = array(
       
'label' => t('Rating'),
       
'description' => t('Fivestar module form elements.'),
       
'weight' => 0,
      );

     
     
// Add a display component to expose new "field" into
      // "manage display" bundle administration
     
$extra['node'][$bundle]['display']['wfivestar_form'] = array(
       
'label' => t("Fivestar (editor's rate)"),
       
'description' => t("Editor Fivestar's rate."),
       
'weight' => 0,
      );
     
    }
   
   
// Test if wfivestar is enabled for current bundle for display
   
$display_values = variable_get('wfivestar_'.$bundle.'_display', array('state' => FALSE));
    if (
$display_values['state']) {
   
     
// Add a display component to expose new "field" into
      // "manage display" bundle administration
     
$extra['node'][$bundle]['display']['wfivestar_display'] = array(
       
'label' => t('Fivestar'),
       
'description' => t("User fivestar's rate."),
       
'weight' => 1,
      );
   
    }
   
  }
  return
$extra;
}

/**
 * Implements hook_form_FORM_ID_alter
 * to alter the content-type display form
 */
function wfivestar_form_field_ui_display_overview_form_alter(&$form, &$form_state) {
 
 
$extra = wfivestar_field_extra_fields();

 
// If the fields are not supported for the entity type and bundle
  // we're loading the form for then return early.
 
if (!isset($extra[$form['#entity_type']], $extra[$form['#entity_type']][$form['#bundle']])) {
    return;
  }
 
 
// Filter extra fields by those the field ui module is chosing to show in the form.
 
$extra_fields = array_keys($extra[$form['#entity_type']][$form['#bundle']]['display']);
 
$extra_fields = array_intersect($extra_fields, $form['#extra']);
 
  if (empty(
$extra_fields)) {
    return;
  }
 
 
// Retrieve fivestar widgets
 
$widgets = module_invoke_all('fivestar_widgets');
 
 
// Get current settings for this entity/bundle
  // (@see form_submit : I store additional settings
  // into this variable to avoid create another variable
  // in variable table)
 
$settings = field_bundle_settings($form['#entity_type'], $form['#bundle']);
 
 
// Retrieve current view-mode
 
$view_mode = $form['#view_mode'];

  foreach (
$extra_fields as $field_name) {
   
   
// Get my own settings from "settings" key
   
$wfivestar_settings = isset($settings['extra_fields']['display'][$field_name],
       
$settings['extra_fields']['display'][$field_name][$view_mode],
       
$settings['extra_fields']['display'][$field_name][$view_mode]['settings'])
      ?
$settings['extra_fields']['display'][$field_name][$view_mode]['settings']
      : array();
   
   
// Get current format (hidden or visible, to
    // show settings form when needed only)
   
$format_type = isset($form_state['values'],
       
$form_state['values']['fields'],
       
$form_state['values']['fields'][$field_name],
       
$form_state['values']['fields'][$field_name]['type'])
        ?
$form_state['values']['fields'][$field_name]['type']
        :
$form['fields'][$field_name]['format']['type']['#default_value'];
   
   
// Build the settings form. Here, the "settings_edit"
    // is the crucial key to use. You must start with
    // it and build other settings fields into.
   
if ($format_type == 'visible') {
     
$form['fields'][$field_name]['settings_edit'] = array(
       
'#type' => 'fieldset',
       
'#collapsible' => TRUE,
       
'#collapsed' => TRUE,
       
'#title' => t('Fivestar settings'),
       
//'#attributes' => array('class' => array('field-formatter-settings-edit-form')),
        //'#parents' => array('fields', $field_name, 'settings_form'),
 
       
'style' => array(
         
'#type' => 'select',
         
'#title' => t('Value to display as stars'),
         
'#options' => array(
           
'average' => t('Average vote'),
           
'user'    => t("User's vote"),
           
'smart'   => t("User's vote if available, average otherwise"),
           
'dual'    => t("Both user's and average vote"),
          ),
         
'#default_value' => isset($wfivestar_settings['style'])
            ?
$wfivestar_settings['style']
            :
'average',
        ),
       
'text' => array(
         
'#type' => 'select',
         
'#title' => t('Text to display under the stars'),
         
'#options' => array(
           
'none'    => t('No text'),
           
'average' => t('Average vote'),
           
'user'    => t("User's vote"),
           
'smart'   => t("User's vote if available, average otherwise"),
           
'dual'    => t("Both user's and average vote"),
          ),
         
'#default_value' => isset($wfivestar_settings['text'])
            ?
$wfivestar_settings['text']
            :
'none',
        ),
       
'widget' => array(
         
'#tree' => TRUE,
         
'#type' => 'fieldset',
         
'#title' => t('Star display options'),
         
'#description' => t('Choose a style for your widget.'),
         
'#collapsible' => TRUE,
         
'#collapsed' => TRUE,
         
'fivestar_widget' => array(
           
'#type' => 'radios',
           
'#options' => array('default' => t('Default')) + $widgets,
           
'#default_value' => isset($wfivestar_settings['widget'], $wfivestar_settings['widget']['fivestar_widget'])
              ?
$wfivestar_settings['widget']['fivestar_widget']
              :
'default',
           
'#attributes' => array('class' => array('fivestar-widgets', 'clearfix')),
           
'#pre_render' => array('fivestar_previews_expand'),
           
'#attached' => array('css' => array(drupal_get_path('module', 'fivestar') . '/css/fivestar-admin.css')),
          ),
        ),
      );
     
$form['fields'][$field_name]['settings_summary']['#markup'] = 'This is the settings summary.';
    }
  }
 
// Don't forget to add a submit handler if you need to process/save
  // configuration for your fields.
 
$form['#submit'][] = 'wfivestar_form_field_ui_display_overview_form_submit';
}

/**
 * Custom submit callback to store
 * settings
 */
function wfivestar_form_field_ui_display_overview_form_submit($form, &$form_state) {
 
 
// Get current module extra fields
 
$extra = wfivestar_field_extra_fields();
 
// Filter extra fields by those the field ui module is chosing to show in the form.
 
$extra_fields = array_keys($extra[$form['#entity_type']][$form['#bundle']]['display']);
 
$extra_fields = array_intersect($extra_fields, $form['#extra']);

 
// Get current settings and view-mode
 
$settings = field_bundle_settings($form['#entity_type'], $form['#bundle']);
 
$view_mode = $form['#view_mode'];
 
 
// Iterate on each extra fields and get
  // current form values
 
foreach ($extra_fields as $field_name) {
   
   
$wfivestar_settings = isset($form_state['values'],
       
$form_state['values']['fields'],
       
$form_state['values']['fields'][$field_name],
       
$form_state['values']['fields'][$field_name]['settings_edit'])
      ?
$form_state['values']['fields'][$field_name]['settings_edit']
      : array();
   
   
// Add current form values into the "settings" key
    // of the current managed field
   
$settings['extra_fields']['display'][$field_name][$view_mode]['settings'] = $wfivestar_settings;
  }
 
 
// Finally, recall field_bundle_settings
  // with settings as last param to save it
  // into the database.
 
$settings = field_bundle_settings($form['#entity_type'], $form['#bundle'], $settings);
}
?>

Like you see in the custom submit callback, I save my own settings into the entity-type/bundle settings with the field_bundle_settings method. When data are saved, I can retrieve and use it into the hook_form_FORM_ID_alter.

With this implementation, I have a "settings" fieldset in regard of my extra field and I can expand it to change settings. It don't work exactly like the real field settings system (with a button who display a form reloaded by ajax) but I can set, save and retrieve data as I want. With some kind of CSS, it would be possible to show a button instead of a "Settings" fieldset title and better mimic the standard behavior.

Special thanks to Josh Waihi to show me the way : http://www.joshwaihi.com/content/working-extra-fields-drupal-7

Hope this help.

Here's a snippet to hide newly created extra fields when they're defined for the first time. With it, you won't need to wait on the patch mentioned above or patch core yourself.

<?php
/**
 * Utility function to hide any newly created extra_fields.
 *
 * @param $entity_type string
 *   Entity type on which the new field will be displayed.
 *
 * @param $bundle string
 *   Bundle on which the new field will be displayed.
 *
 * @param $field_name string
 *   Machine name of extra field.
 */
function mymodule_hide_new_extra_field($entity_type, $bundle, $field_name) {
 
$settings = field_bundle_settings($entity_type, $bundle);
 
$settings_updated = FALSE;
 
$entity_info = entity_get_info($entity_type, $bundle);
 
$view_modes = array_keys($entity_info['view modes']);
  if (!
in_array('default', $view_modes)) $view_modes[] = 'default';
  foreach (
$view_modes as $view_mode) {
    if (!isset(
$settings['extra_fields']['display'][$field_name][$view_mode])) {
     
$settings_updated = TRUE;
     
$settings['extra_fields']['display'][$field_name][$view_mode] = array(
       
'weight' => 100,
       
'visible' => FALSE,
      );
    }
  }
  if (
$settings_updated) {
   
field_bundle_settings($entity_type, $bundle, $settings);
  }
}
?>

If you looking for the Drupal 8 version of this function, then this is the hook_entity_extra_field_info() .