Skip to main content

#group attribute in Drupal 8 Form API. Explained

Feb 13, 2020

I am not sure whether you ever seen the tiny #group property located on the iconic Form API guide. Most likely, you’ve never needed to.

During our effort of integrating Product Builder with the Field Group module, I’ve got stuck with understanding this property. The FAPI documentation clearly states that this property is being used to group fieldsets into vertical tabs. Like a clearly single-purpose option.

Specifies a group that can be used to group fieldsets into vertical tabs.

Despite its single purpose, I’ve noticed two things in a Field Group module:

  1. It does not have any place where form elements are being nested/grouped in each other.
  2. Field Group heavily uses the #group property which I was not aware of at the time. And clearly, this was not for fieldsets only.

I needed Field Group integration so badly and started an investigation without having much of a choice. I always loved this about Drupal. Just spend a half an hour scrolling through some random code and you will find out some peculiar undocumented features.

 

What the hell is going on?

After not-so-short research, it appeared that #group property can be used instead of nesting child elements into parent containers, fieldsets, etc. This feature was committed in the core approximately 7 years ago (proof: https://www.drupal.org/project/drupal/issues/1856178) and despite there is no public documentation about it.

Even more, when you try to google something like “#group FAPI Drupal 8”, you always land on tutorials for Group module.

Examples of #group property usage:

$form['fieldset_new'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Fieldset'),
];

$form['message'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Message'),
      '#required' => TRUE,
      '#group' => 'fieldset_new',
];

For those, who have forgotten, the old-good nesting approach is the following:

$form['fieldset_classic'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Fieldset'),
];

$form['fieldset_classic']['message'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Message Nested'),
      '#required' => TRUE,
];

Both snippets shown above are nearly identical and result in the same kind of HTML markup. The main difference is relevant for developers only since the #group approach makes the code much more predictive for long-term projects.

Pros of this #group approach:

  • Quite often, changing a form element hierarchy can cause unexpected issues with third-party form_alters. If someone’s form_alter relies on a given form hierarchy, any changes to it can be quite destructive. Especially, when the project isn’t documented properly (and tell me, which project is). Using a flat structure and #group approach eliminates these sorts of issues.
  • Now the same approach is being used by the Views UI module and Field Group which means following it makes your code a bit more consistent with titans of the Drupal world.

Cons

  • The most important disadvantage – it’s poorly documented and your coworkers might not understand what your code is doing. And official documentation won’t help them to understand.
  • Debugging forms grouped by #group property isn’t as nice as nested form elements. It’s quite visual to see nested arrays and understand which elements included in a certain container.

So what now?

Both approaches are good enough to be used. Though the more I analyze, the more I come to the conclusion that the #group approach might lead to a more robust code in the end.

Hopefully, after this article, you will have less undiscovered space left in the Drupal Form API.

Update from a reader

Unfortunately, #group property does not work in 100% of the cases (at least, without additional workarounds). This happens due to the core issue #2190333

Hopefully, it gets fixed soon.