How do I set group fields in pairs? The code is taken from the "Form API examples" and supplemented. There must be a pair of first and last name fields. A button “Add more” should add a new pair of fields “name and surname” below the existing one. This works (a little later using css so that they are on the same line), but they are grouped incorrectly. They are displayed following this order.


Name
Name
Name
Surname
Surname
Surname


I need to get them in the following order. (Using CSS, I will inline them.)


Name
Surname
Name
Surname
Name
Surname


What am I doing wrong?

The code I am using is the following one.

public function buildForm(array $form, FormStateInterface $form_state) {
    $form['description'] = [
      '#type' =&gt; 'item',
      '#markup' =&gt; $this-&gt;t('This example shows an add-more and a remove-last button.'),
    ];

    // Gather the number of names in the form already.
    $num_names = $form_state-&gt;get('num_names');
    // We have to ensure that there is at least one name field.
    if ($num_names === NULL) {
      $name_field = $form_state-&gt;set('num_names', 1);
      $num_names = 1;
    }

    $form['#tree'] = TRUE;
    $form['names_fieldset'] = [
      '#type' =&gt; 'fieldset',
      '#title' =&gt; $this-&gt;t('People coming to picnic'),
      '#prefix' =&gt; '&lt;div id="names-fieldset-wrapper"&gt;',
      '#suffix' =&gt; '&lt;/div&gt;',
    ];

    for ($i = 0; $i &lt; $num_names; $i++) {
      $form['names_fieldset']['name'][$i] = [
        '#type' =&gt; 'textfield',
        '#title' =&gt; $this-&gt;t('Name'),
      ];
      $form['names_fieldset']['surname'][$i] = [
        '#type' =&gt; 'textfield',
        '#title' =&gt; $this-&gt;t('Surname'),
      ];
    }

    $form['names_fieldset']['actions'] = [
      '#type' =&gt; 'actions',
    ];
    $form['names_fieldset']['actions']['add_name'] = [
      '#type' =&gt; 'submit',
      '#value' =&gt; $this-&gt;t('Add one'),
      '#submit' =&gt; ['::addOne'],
      '#ajax' =&gt; [
        'callback' =&gt; '::addmoreCallback',
        'wrapper' =&gt; 'names-fieldset-wrapper',
      ],
    ];
    // If there is more than one name, add the remove button.
    if ($num_names &gt; 1) {
      $form['names_fieldset']['actions']['remove_name'] = [
        '#type' =&gt; 'submit',
        '#value' =&gt; $this-&gt;t('Remove One'),
        '#submit' =&gt; ['::removeCallback'],
        '#ajax' =&gt; [
          'callback' =&gt; '::addmoreCallback',
          'wrapper' =&gt; 'names-fieldset-wrapper',
        ],
      ];
    }
    $form['actions']['submit'] = [
      '#type' =&gt; 'submit',
      '#value' =&gt; $this-&gt;t('Submit'),
    ];

    return $form;


The form is rendered as in this screenshot.


  


The second part of the code where the code is processed

  public function submitForm(array &amp;$form, FormStateInterface $form_state) {
    $values_1 = $form_state-&gt;getValue(['names_fieldset', 'name']);
    $values_2 = $form_state-&gt;getValue(['names_fieldset', 'surname']);

    for ($i = 0; $i &lt; count($values_1); $i++) {
      $arr[] = $values_1[$i] . ' ' . $values_2[$i];
    }

    $output = $this-&gt;t('These people are coming to the picnic: @names', [
      '@names' =&gt; implode(', ', $arr),
    ]
    );
    $this-&gt;messenger()-&gt;addMessage($output);
  }
You could add a fieldset for each person, something like this should work:

for ($i = 0; $i &lt; $num_names; $i++) {
  $form['names_fieldset']['person_' . $i] = [
    '#type' =&gt; 'fieldset',
    '#title' =&gt; $this-&gt;t('Person ' . $i),
  ];

  $form['names_fieldset']['person_' . $i]['name'] = [
    '#type' =&gt; 'textfield',
    '#title' =&gt; $this-&gt;t('Name'),
  ];
  $form['names_fieldset']['person_' . $i]['surname'] = [
    '#type' =&gt; 'textfield',
    '#title' =&gt; $this-&gt;t('Surname'),
  ];
}


And then alter your remove callback and a few other bits to match