Play with Display: Altering Drupal Field Collections

#Drupal | Posted

How do you insert a text field into a rendered entity, all within a field collection? This question came to mind on a recent Drupal 7 build. What at first sounded complicated turned into a concise and elegant solution.

The basis of the requirement broke down into the need for events to list those participating in the event. The build already had existing Event and Profile content types, so the new functionality would only require implementing the list of Profiles participating in an event. Each participant would include their role at the event, along with links to contact them.

Event Participant Example

To implement this, an Event Participant field collection was added as a field to the Event content type. Each instance of the field collection included a single Profile content type and a single text field for their role at the Event. The Profile would then be displayed as a rendered entity within the field collection so that the Profile’s image and contact links could be displayed. The point at issue then arose. How would the Participant’s role at the event be placed within the rendered Profile teaser, along with the other Profile fields? To the user it needed to look like a single unified entity, with the Profile’s image, role at the Event, Profile name, and contact links all included.

Screen Shot 2014-12-11 at 4.27.43 PM.png

After some initial brainstorming a handful of different approaches came to mind. The initial impression was to use template_preprocess_field() since it allows the alteration of values in the theme layer. The participant role field would be removed from it’s initial location and inserted into the participant profile object. This did not work though. Despite the repositioning of elements within the render array, the changes occur too late in the rendering execution. The render array had already been rendered and thus the display remained the same.

After quickly realizing a theme function wasn’t the answer, hook_node_view() came to mind. The hook allows for the alteration of a node before it is rendered and so all that would be needed would be to shift the location of the Participant Role field within the $node array. The issue with this implementation and the reason it wasn’t chosen, is that it would require reaching out to the global state to determine what changes are needed. In other words hook_node_view() doesn’t realize what context it is called under and would thus require a hack of sorts to determine when it was being called under an Event node. This could be worked around by checking the URL path, but this is not best practice as it is brittle and prone to breaking with future site updates.

As they say the third time’s the charm. The final solution was to use hook_field_collection_item_view(). This hook allows for the alteration of a field collection item before rendering. Unlike many of the other options, this hook is called for each field collection item, allowing one to both check the field collection name and adjust it’s internal field ordering, before any rendering occurs. Using this hook, each Event Participant field collection item is altered, removing the Role field and inserting it into the Profile rendered entity. Simple as that. Now each Event Participant is correctly displayed as a single unified entity, with an image, name, and a set of contact links.

  1. function hook_event_field_collection_item_view($field_collection_item, $view_mode, $langcode) {
  2. if ($field_collection_item->field_name == "field_event_participants") {
  3. $role = $field_collection_item->content['field_event_participants_role'];
  4. $profile_id = $field_collection_item->field_event_participants_profile[LANGUAGE_NONE][0]['target_id'];
  6. // Remove Participant Role from previous location in field collection output
  7. unset($field_collection_item->content['field_event_participants_role']);
  9. // Insert Participant Role into the Participant Profile output
  10. $field_collection_item->content['field_event_participants_profile'][0]['node'][$profile_id]['field_event_participants_role'] = $role;
  11. }
  12. }

Through several quick, small, iterations an elegant, straight-forward solution was found to what at first sounded like a complicated feature request. Now users viewing an Event can see a list of Participants, with each Participant displayed as a single item. Check out more on field collections on the Phase2 blog!