The previous lesson's buildForm() returned an empty array. This lesson fills it in — the actual work of describing a form: what fields exist, what they're called, what type they are. Every key you add to that returned array is one more piece of the form Drupal will render.
What you'll learn in this lesson
- How array keys become field machine names
- The
#typeproperty, and why it's the one every element needs - Reading submitted values back out via the array key
Building the form array
public function buildForm(array $form, FormStateInterface $form_state) {
$form['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Your name'),
'#required' => TRUE,
];
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('Your email'),
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send'),
];
return $form;
}
Each top-level key (name, email, actions) becomes that element's machine name — this is exactly what you'll later use to read the submitted value back out with $form_state->getValue('name'). The key you choose here isn't cosmetic; it's the identifier for that field everywhere else in your form class.
Why actions wraps the submit button
Wrapping the submit button in an actions container (itself a render element of #type => 'actions') is a Drupal convention, not a strict requirement — it lets themes consistently style the button area separately from the fields above it, and lets multiple buttons (Submit, Cancel, Save Draft) sit together in one predictable place.
#type. Without it, Drupal has no idea what to render — #type => 'textfield' versus 'email' versus 'select' is what determines the actual HTML input element, its validation behavior (an email type gets automatic format validation you didn't write), and which other #-properties are even meaningful for that element.Reading values back
public function submitForm(array &$form, FormStateInterface $form_state) {
$name = $form_state->getValue('name');
$email = $form_state->getValue('email');
// The string 'name' here must match the array key from buildForm() exactly.
}
Quick check: you rename the array key from
'name'to'full_name'inbuildForm()but forget to updatesubmitForm()'sgetValue('name')call. What happens? If you saidgetValue('name')returns NULL since that key no longer exists in the submitted values, you've understood exactly why the array key is a real identifier connecting buildForm() and submitForm(), not just a label.
Key takeaways
- Each top-level array key in
buildForm()'s return value becomes that field's machine name, used later to read its value. - Every form element needs a
#type— it determines the rendered HTML, built-in validation, and which other properties apply. - Wrapping buttons in an
actionscontainer is a theming convention, letting themes style the button area consistently. - Field keys in
buildForm()andgetValue()calls insubmitForm()must match exactly — a rename in one without the other silently breaks.
Coming up next
You've built a form's structure. Next, we look at the service responsible for actually turning that array into HTML and processing it — the form builder.