hook_form_alter() to put multiple radios in a table I have a content type that has a series of multi-value select fields. That I need users to select in relation to a value pulled from another node.

The best way to present this is to have a table that looks like this:



I've got the 'bones' of a hook_form_alter, and I can almost present what I need. However, I cannot seem to get my checkboxes/radios to appear in the grid. I am this far...



My form_alter function looks like:

function hnbuilder_form_node_scheme_of_work_form_alter(&amp;$form, FormStateInterface &amp;$form_state, &amp;$form_id){
/** check whether we passed a node reference into the form via ID in the url **/
if ($_GET['id']){
    $nid = $_GET['id'];
    /** if we didn't pass it in, we are editing the node **/
}else{
    $current_url = Url::fromRoute('&lt;current&gt;');
    $path = $current_url-&gt;getInternalPath();
    $path_args = explode('/', $path);
    $thisnid = $path_args[1];
    $thisnode = \Drupal\node\Entity\Node::load($thisnid);
    $nid = $thisnode-&gt;get('field_related_unit')-&gt;target_id;
}
/** Load the referenced node */
$node = \Drupal\node\Entity\Node::load($nid);

/** get the learning outcomes from the referenced node 
    and put them into an array **/
$lo = array(
    0=&gt;"",
    1=&gt;"LO1 ".$node-&gt;field_lo1-&gt;value,
    2=&gt;"LO2 ".$node-&gt;field_lo2-&gt;value,
    3=&gt;"LO3 ".$node-&gt;field_lo3-&gt;value,
    4=&gt;"LO4 ".$node-&gt;field_lo4-&gt;value
);

/** create table headers **//
$header = array(
    0 =&gt; 'Learning Outcome',
    1 =&gt; 'Assignment 1',
    2 =&gt; 'Assignment 2',
    3 =&gt; 'Assignment 3',
    4 =&gt; 'Assignment 4'
);

/** an array of the checkbox/radio options from our content type
    each field has 4 options
*/

$ffld = array(
    1=&gt;$form['field_lo1_sow']['widget']['#options'],
    2=&gt;$form['field_lo2_sow']['widget']['#options'],
    3=&gt;$form['field_lo3_sow']['widget']['#options'],
    4=&gt;$form['field_lo4_sow']['widget']['#options']
);

/** create a new array of options for the table
    */

$options = array(
    1=&gt;array($lo[1],$ffld[1][1],$ffld[1][2],$ffld[1][3],$ffld[1][4]),
    2=&gt;array($lo[2],$ffld[2][1],$ffld[2][2],$ffld[2][3],$ffld[2][4]),
    3=&gt;array($lo[3],$ffld[3][1],$ffld[3][2],$ffld[3][3],$ffld[3][4]),
    4=&gt;array($lo[4],$ffld[4][1],$ffld[4][2],$ffld[4][3],$ffld[4][4])
);

/** build the tableselect **/
$form['table'] = array(
    '#type' =&gt; 'tableselect',
    '#header' =&gt; $header,
    '#options' =&gt; $options,
    '#js_select' =&gt; FALSE
    );

return $form;


}

I think I have several issues, and I'm hoping someone can help:


I'm not sure that 'tableselect' is the right thing to be using, as it seems this only supports single selection (based on the table row).
I'm unsure how to display the existing checkbox/radios as actual field items. I'm stuck getting their values.


Is there a way to achieve this? If so, I'd really appreciate some pointers.
I think you can define a wrapper element as type table. The core user permissions form does this, and I think is similar to what you're trying to build:

 $form['permissions'] = [
  '#type' =&gt; 'table',
  '#header' =&gt; [
    $this-&gt;t('Permission'),
  ],
  '#id' =&gt; 'permissions',
  '#attributes' =&gt; [
    'class' =&gt; [
      'permissions',
      'js-permissions',
    ],
  ],
  '#sticky' =&gt; TRUE,
];
foreach ($role_names as $name) {
  $form['permissions']['#header'][] = [
    'data' =&gt; $name,
    'class' =&gt; [
      'checkbox',
    ],
  ];


In your case, I think you'd define something like $form['sow'] as a '#type' =&gt; 'table' which will be a wrapper element that all the checkbox (or radios) appear under.

Then loop through your fields (these will be similar to how the user permission form loops through each permission, creating a row).

foreach ($fields as $low) {
  $form['sow'][$low]['description'] = [
    '#type' =&gt; 'item',
    '#title' =&gt; $low['title'],
    '#description' =&gt; ...,
  ];

  // Now loop through each assignment. This will be similar
  // to how the user permission form loops through each role
  // for each permission.
  foreach ($assignments as $assignment) {
    $form['sow'][$low][$assignment] = [
      '#type' =&gt; 'checkbox', // Or 'radio', depending on the need.
      // Other form element details here too.
    ];
  }


Then, for each assignment (these will behave similar to each role in the user permission form)