Author image
Senior Developer

Add stuff to a node and configure it like fields

We often want to add things to the content of a node or any other entity in Drupal 7 using hook_node_view(), hook_node_view_alter() or a similar hook in a custom module. This could be anything from a custom social media link, a field rendered in a custom way, additional author information or virtually anything else.

The rendered node with our pseudo-field

Here's the code used to add that Facebook like button:

/**
* Implements hook_node_view().
*
* Adds a Facebook like button to page nodes.
*/
function MYMODULE_node_view($node, $view_mode, $langcode) {
  switch ($node->type) {
    case 'page':
      $node->content['MYMODULE_fb_like'] = array(
        '#type' => 'item',
        '#title' => t('Like this content: '),
        '#markup' => '<fb:like send="false" layout="button_count" width="90" show_faces="false" href="' . url(current_path(), array('absolute' => TRUE)) . '"></fb:like>',
      );
      break;
  }
}

These extra items nestle in alongside fields on the node, so can be considered 'pseudo-fields', but on their own are totally inflexible as they can't be repositioned and seem to appear out of nowhere onto the page. You should give yourself or site owners/administrators as much power as possible to customize content - and there is an easy solution here, to allow your extra content to be repositioned in exactly the same way as standard fields in the manage fields/display UI can be.

Simply implement hook_field_extra_fields() in your custom module, and your pseudo-fields will appear alongside fields in those administrative screens like this:

The manage fields UI

You declare your pseudo-fields (or 'extra fields') to the Fields API so that it can check the rendered nodes for you and set the orders of fields and your extra content according to the field display settings rather than just the defaults that you hard-wired in code. You specify which entities and which bundles your pseudo-fields are attached to, and whether they are on the displayed content, or on the edit/create form for the type of content. For the FB Like example above, here's what we do:

/**
* Implements hook_field_extra_fields().
*
* Declare our Facebook Like button as a pseudo-field.
*/
function MYMODULE_field_extra_fields() {
  // The levels of the array that we return correspond to the
  // entity type, bundle and then either 'display' or 'form'.
  // In this case, we apply to 'page' nodes, when we display them.
  $extra['node']['page']['display'] = array(
    // The keys here must correspond to the keys of the items
    // that we add in our hook_node_view() or similar function.
    // Prefix it with our module name to ensure it doesn't clash
    // with anything from other modules.
    'MYMODULE_fb_like' => array(
      'label' => t('Custom FB Like button'),
      'description' => t('Facebook like button, added in MYMODULE_node_view().'),
      'weight' => 10,
    ),
  );
  return $extra;
}

Some brief explanation of the parts you specify in the hook_field_extra_fields():

  • Either specify 'display' or 'form' depending on whether your extra content is shown on the displayed content (which is what we do in this example), or the create/edit forms of the content. Most of this article is talking about adding pseudo-fields to the display of content, but have a look at the Domain Access module as an example of an extra item on the form -- see domain_field_extra_fields() -- which it uses to add the domain settings for specific nodes.

  • The label is only what is shown in the manage fields/display settings screen, it is not used on the actual content.

  • The description doesn't seem to get used anywhere so is useful as documentation for anyone that reads your code. I would recommend referring to which function adds the pseudo-field to the content as that's not always obvious.

  • If you've added the hooks above to your own module, make sure you replace 'MYMODULE' with your own module name, and flush your site caches for Drupal to pick them up (or just enable the module).

Part of the joy of using hook_field_extra_fields is that it will work with other field management tools like Field Group. You can place your pseudo-fields within field groups painlessly this way. Here's an example (click the images to see them in full):

Extra fields within a node using field groupManage extra fields and field groups

There are of course some alter hooks for extra fields: hook_field_extra_fields_alter() and hook_field_extra_fields_display_alter().

If you wanted to export these display settings with something like features, the giant field_bundle_settings variable stores all the settings for all entities / types / bundles / view modes.

Note that all this applies to any fieldable entity, so you can do the same for users, taxonomy terms, etc!

While this article has focussed on Drupal 7, the same things can be achieved in Drupal 6 with CCK and hook_content_extra_fields().

Comments

This is hugely helpful. I've been trying to marry my custom fields into the UI precisely to get them into Field Groups without having to set #group_children elements and such in hook_node_view(), which gets very messy very fast.

Do you know how to get this to also display the label as described in hook_field_extra_fields()? That would be the final piece to the puzzle for me.

Let me revise my comment... Is there a more sane way of doing this:

$node->content['mymodule_pseudo_field'] = array(
'#prefix' => '<div class="field field-label-inline clearfix"><div class="field-label">My Pseudo Field</div>',
'#suffix' => '</div>',
'#markup' => '<div class="field-items">' . $my_pseudo_value . '</div>',
);

Charlie, yes use the 'item' element type -- see http://api.drupal.org/api/drupal/developer%21topics%21forms_api_referenc...

So:

$node->content['mymodule_pseudo_field'] = array(
  '#type' => 'item',
  '#title' => t('My Pseudo Field'),
  '#markup' => $my_pseudo_value,
);

Note that the label you define in hook_field_extra_fields() is meant for administrators to see on the Fields UI screen, it's not meant to necessarily be the actual title shown on your content.

That's awesome, I never thought of mixing form elements into my node's render arrays, but they certainly work just fine. Only problem with "item" is that I can't specify the label to display inline.

In response to your last point, I would think the label should be allowed to be displayed ("Display/Hidden" on the fields display page) since that allows the label to be hidden if it's for administrative purposes only.

Agreed! The label is only 'meant' to be administrative, but you can of course use it. To make your label appear inline, you could try messing around with adding '#theme_wrappers' => 'field', '#label_display' => 'inline' (or something like that) to your render array but that's not so simple -- just add some theming for your specific label. There's no in-built way to set the label display in the UI otherwise, since these custom elements we add may or may not have a label, the Fields UI just has no way of knowing.

Render arrays are awesome -- form elements are just render arrays, there's nothing special about them. I use them everywhere now - it keeps everything easily alterable :-)

Comments on this article are now closed, if you want to give us feeback you can use our contact form instead.