Validation was the bouncer checking IDs at the door; submitForm() is what happens once someone's actually inside — the point where a form stops being a UI exercise and starts doing real work: saving a record, sending an email, redirecting somewhere new.
What you'll learn in this lesson
- How to read submitted values out of
$form_state - What you can do inside
submitForm()— persist data, send a message, redirect - Why
submitForm()never needs to validate anything itself - How to redirect the visitor somewhere after a successful submission
A real submit handler
public function submitForm(array &$form, FormStateInterface $form_state) {
$title = $form_state->getValue('title');
// Do the real work — here, just a confirmation message.
$this->messenger()->addMessage($this->t('You specified a title of %title.', [
'%title' => $title,
]));
}
By the time submitForm() runs, every value has already passed validateForm() — there's no need to re-check anything here. Its only job is reading the now-trusted values with $form_state->getValue('key') and acting on them.
What "acting on them" usually means
- Saving to a database table or a content/config entity
- Setting a configuration value with
$this->configFactory()->getEditable('your_module.settings')->set('key', $value)->save() - Sending an email via the
plugin.manager.mailservice - Showing a confirmation with
$this->messenger()->addMessage() - Redirecting the visitor with
$form_state->setRedirect('route.name')
Redirecting after submission
$form_state->setRedirect('your_module.confirmation_page');
Without an explicit redirect, Drupal reloads the same form after submission — fine for a simple settings form, but often not what you want after, say, creating a new record. setRedirect() takes a route name (plus an optional array of route parameters), sending the visitor somewhere more useful once the work is done.
submitForm() with no error handling. If it's the kind of operation that might legitimately fail after validation has already passed, wrap it in a try/catch and use $this->messenger()->addError() to tell the visitor what went wrong, rather than letting an uncaught exception produce a raw error page.Quick check: does
submitForm()need to check whether the title field is empty, given that validation already ran? No — by the timesubmitForm()executes, every value in$form_statehas already satisfied every check invalidateForm(). Re-checking there would be redundant; trust the values.
Key takeaways
submitForm()only runs after every check invalidateForm()has passed — treat its input as already trusted.$form_state->getValue('key')is how you read a submitted value, using the same key defined inbuildForm().- Use
$form_state->setRedirect('route.name')to send the visitor somewhere other than the same form after submission. - Wrap risky operations (external calls, file handling) in try/catch even inside a validated submit handler — validation doesn't guarantee the *outside world* will cooperate.
Coming up next
Every form so far reloads the whole page on submit. Next, we make a form reactive — using AJAX to update part of the page without a full reload.