I was experiencing an error with nested groups not being rendered. So I debugged the code down to the field_group_fields_nest() function. Knowing the source of the problem I looked for updates. The current dev of this module does solve the error I experienced but I had already seen that this function could be written a lot easier.

So I decided to rewrite the function and post it here as an improvement:

function field_group_fields_nest(&$root_element) {
  // Create all groups and keep a flat list of references to these groups.
  $group_references = array();
  foreach ($root_element['#groups'] as $group_name => $group) {
    $root_element[$group_name] = array();
    $group_references[$group_name] = &$root_element[$group_name];
  }

  // Move all children to their parents. Use the flat list of references for
  // direct access as we don't know where in the root_element hierarchy the
  // parent currently is situated.
  foreach ($root_element['#group_children'] as $child_name => $parent_name) {
    // Block denied fields (#access) before they are put in groups.
    if (!isset($root_element[$child_name]['#access']) || $root_element[$child_name]['#access']) {
      // Use a reference as this may be a group, not a field.
      $group_references[$parent_name][$child_name] = &$root_element[$child_name];
    }
    // The child has been copied to its parent: remove it from the root element.
    unset($root_element[$child_name]);
  }

  // Bring extra element wrappers to achieve a grouping of fields.
  // This will mainly be prefix and suffix altering.
  foreach ($root_element['#groups'] as $group_name => $group) {
    field_group_pre_render($group_references[$group_name], $group, $root_element);
  }
}

Advantages;
- no nested loops.
- no recursion.
- (consequence of the first 2) more performant.
- field_pre_render called when the whole hierarchy has been built.
- less side effects: #group_children (and #groups) array elements remain unaffected.

I think this will be a nice improvement of your code.

Comments

fietserwin’s picture

Further improving the code:
- the calling function can be cleaned
- documentation adapted to the new code

function field_group_build_pre_render($element) {

  // Merge our #fieldgroups with #groups to avoid conflicts on fieldset types.
  $element['#groups'] = array_merge($element['#groups'], $element['#fieldgroups']);

  // Nest the fields in the corresponding field groups.
  field_group_fields_nest($element);

  // Allow others to alter the pre_rendered build.
  drupal_alter('field_group_build_pre_render', $element);

  return $element;
}

/**
 * Builds the element hierarchy based on the defined grouping (as can be found
 * in the #group and #group_child elements of the given element).
 *
 * @param array $element
 *   The element that is to be regrouped hierarchically.
 */
function field_group_fields_nest(&$element) {
  // Create all groups and keep a flat list of references to these groups.
  $group_references = array();
  foreach ($element['#groups'] as $group_name => $group) {
    $element[$group_name] = array();
    $group_references[$group_name] = &$element[$group_name];
  }

  // Move all children to their parents. Use the flat list of references for
  // direct access as we don't know where in the root_element hierarchy the
  // parent currently is situated.
  foreach ($element['#group_children'] as $child_name => $parent_name) {
    // Block denied fields (#access) before they are put in groups.
    if (!isset($element[$child_name]['#access']) || $element[$child_name]['#access']) {
      // If this is a group, we have to use a reference to keep the reference
      // list intact (but if it is a field we don't mind).
      $group_references[$parent_name][$child_name] = &$element[$child_name];
    }
    // The child has been copied to its parent: remove it from the root element.
    unset($element[$child_name]);
  }

  // Bring extra element wrappers to achieve a grouping of fields.
  // This will mainly be prefix and suffix altering.
  foreach ($element['#groups'] as $group_name => $group) {
    field_group_pre_render($group_references[$group_name], $group, $element);
  }
}
Stalski’s picture

We'll give it a go. The code is looking good and I think this might work for all cases.
I'll respond asap.

Stalski’s picture

Seems to work just fine. The memory usage and query time is the same but the new code looks better and easier to maintain.
The recursion by reference is easier to read. So thx!

Stalski’s picture

Status: Needs review » Fixed

Did some additional testing. No problem what so ever.
Fixed and committed.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.