Propeople Blog

A regular dose of fresh news, events, products, Drupal development resources and more.

July 17, 2013

How to add entity fields to a form in Drupal 7

While working at a Drupal Project, I have encountered various situations when I had to create custom forms and attach entity fields (user, node, comments, commerce_product, etc.) to them.

Creating the forms manually can be headaching activity as it can turn out a buggy result. Since, I assume, Drupal developers work with form entity fields quite often and are in constant search of useful approaches I decided to provide two examples that, I hope, will make your task easier.

The form elements for the entity's fields are added by reference as direct children in the $form parameter. This parameter can be a full form structure (most common case for entity edit forms), or a sub-element of a larger form.

By default, submitted field values appear at the top-level of $form_state['values']. A different location within $form_state['values'] can be specified by setting the '#parents' property on the incoming $form parameter. Because of name clashes, two instances of the same field cannot appear within the same $form element, or within the same '#parents' space.

For each call to field_attach_form(), field values are processed by calling field_attach_form_validate() and field_attach_submit() on the same $form element.

 

Example 1

Add user entity fields to the form.

<?php function my_user_profile_form($form, &$form_state) { // Get current user from globals. global $user; // Load user entity. $account = user_load($user->uid); // Attache entity user fields to the form. field_attach_form('user', $account, $form, $form_state); } ?>


From array after attach.

 

<?php function my_user_profile_form($form, &$form_state) { // Get current user from globals. global $user; // Load user entity. $account = entity_load_single('user', $user->uid); // Attache entity user fields to the form. field_attach_form('user', $account, $form, $form_state); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save changes'), '#submit' => array('my_user_profile_form_submit'), ); return $form; } /** * Validation from callback. */ function my_user_profile_form_validate($form, &$form_state) { // Get entity from the form. $account = $form['#entity']; // Validate entity. field_attach_form_validate('user', $account, $form, $form_state); // Custom validate... } /** * Submit form callback. */ function my_user_profile_form_submit($form, &$form_state) { // Get entity from the form. $entity = $form['#entity']; // Submit entity with default callbacks. field_attach_submit('user', $entity, $form, $form_state); // Save entity. entity_save($entity); } ?>

 

Example 2

Attach billing and shipping fields to the form.

Shipping and Billing are commerce_customer_profile entity type with 2 bundles: billing and shipping.

 

By applying this example we will achieve the following:

  • Add 2 entity in form.
  • Create separate containers for each entity.
  • Validate and save each entity.

 

<?php function my_user_address_form($form, &$form_state) { // Get current user from globals. global $user; // Create new profile. $profile = commerce_customer_profile_new('shipping', $user->uid); $form['new_shipping'] = array( '#type' => 'container', '#attributes' => array( 'class' => array( 'field-type-new-shipping', 'field-name-field-shipping', 'field-widget-commerce-customer-profile', ), ), // Save values in tree. '#tree' => TRUE, // Set parrents. '#parents' => array('new_shipping'), ); $form['new_shipping']['customer_profile'] = array( '#type' => 'value', '#value' => $profile, ); // Add the field widgets for the profile. field_attach_form('commerce_customer_profile', $profile, $form['new_shipping'], $form_state); // Create new profile. $profile = commerce_customer_profile_new('billing', $user->uid); $form['new_billing'] = array( '#type' => 'container', '#attributes' => array( 'class' => array( 'field-type-new-billing', 'field-name-field-billing', 'field-widget-commerce-customer-profile', ), ), // Save values in tree. '#tree' => TRUE, // Set parrents. '#parents' => array('new_billing'), ); $form['new_billing']['customer_profile'] = array( '#type' => 'value', '#value' => $profile, ); // Add the field widgets for the profile. field_attach_form('commerce_customer_profile', $profile, $form['new_billing'], $form_state); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save changes'), '#submit' => array('my_user_address_form_submit'), ); return $form; } function my_user_address_form_submit($form, &$form_state) { // Get entities form form. $billing = $form_state['values']['new_billing']['customer_profile']; $shipping = $form_state['values']['new_shipping']['customer_profile']; // Notify field widgets. field_attach_submit('commerce_customer_profile', $billing, $form['new_billing'], $form_state); field_attach_submit('commerce_customer_profile', $shipping, $form['new_shipping'], $form_state); // Custom code... // Save new profilies. commerce_customer_profile_save($billing); commerce_customer_profile_save($shipping); } ?>

 

By attaching the entities in form_state we preserve the parent elements together with the entities and fields we are yet to attach. If we have an address field that works with AJAX, the form’s elements will work correctly and the form integrity will remain intact.

It's also worth reading the Propeople article called "How to build a Popup in Drupal for Login or Register form".

That's all for now, thanks for reading this. Share with others on Facebook or Twitter!

0 Responses to this post

Communicate!