Skip to main content

Adding contributed field widgets to custom forms

Back-end Development
User Experience
Drupal

Say you are putting together a custom form to create nodes on your Drupal 7 site.  For whatever reason you have decided NOT to use the built in node creation form.  And maybe your node has custom fields that are beyond what the Drupal Form API defines. I'm going to use the Video Embed Field as an example here.  

In the cases of more complex field types there is a lot more going on then just placing a textfield on the form, even though the resulting HTML is a simple textfield.  Sometimes it is the pre and post processing surrounding that field that make it unique, and recreating all this would not only take time, but the custom code would likely miss some important part of the node saving process for a given field causing it to be inconsistent with other data on the site or how it should be.

So now an example.

$field = field_info_field('field_some_youtube_video');
$instance = field_info_instance('node','field_some_youtube_video','custom_video');
$items = array();
if (isset($node->field_some_youtube_video[LANGUAGE_NONE][0])) {
  $items = $node->field_some_youtube_video[LANGUAGE_NONE];	
}
$form['#parents'] = array(); // a required element to use 'field_default_form' function below.
$video_field = field_default_form('node', $node, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
$form['video'] = $video_field['field_some_youtube_video'];

This creates our field and adds it to our form building callback function.  Note that the '#parents' attribute of the form needs to at least be initiallized with an empty array, or errors will result.  The 'field_default_form' method assumes this attribute is always there... because it is on a node form. But this is only half of what needs to be done.  This puts the field on the form as expected, but it doesn't handle the submission of the form and any special logic that goes along with saving the new video to the node.  

So in the SUBMIT handler, you add the following:

$node = node_load($form_state['values']['nid']);
$node->field_some_youtube_video = $form_state['values']['video'];
field_attach_presave('node', $node);
field_attach_update('node', $node);

This puts the expected value on the node object, and then calls field_attach_presave and field_attach_update to take that field value and run it through the special field handlers that came with the contributed field type.  Of course, this is not the only way to handle this situation, but so far, in my experience, it is the simplest implementation.