Submission Architecture
When to Use
Understand submission handler priority to control execution order. Always redirect after successful submit to prevent form resubmission.
Decision
| Situation |
Pattern |
Why |
| Standard submission |
setRedirect() |
Prevents resubmission on refresh |
| AJAX submission |
disableRedirect() |
Custom response, no page reload |
| External URL |
setRedirectUrl(Url::fromUri()) |
Rare, only for external sites |
| Custom response |
setResponse($response) |
Non-HTML responses |
Submission Handler Priority
Execution Order (if validation passes):
1. Button-specific #submit handlers (if that button clicked)
2. Form-level #submit handlers ($form['#submit'])
3. Form class submitForm() method
Skipped if $form_state->setRebuild(TRUE) called
Pattern
// Standard submit
public function submitForm(array &$form, FormStateInterface $form_state) {
$value = $form_state->getValue('field_name');
// Process data
$this->doSomething($value);
// Always redirect (prevents resubmission)
$form_state->setRedirect('route.name', ['param' => $id]);
}
// Button-specific handler
$form['actions']['save_continue'] = [
'#type' => 'submit',
'#value' => $this->t('Save and Continue'),
'#submit' => ['::saveContinue'],
];
public function saveContinue(array &$form, FormStateInterface $form_state) {
// Custom handling
$this->save($form_state->getValues());
$form_state->setRedirect('mymodule.next_step');
}
// AJAX submission
public function ajaxSubmit(array &$form, FormStateInterface $form_state) {
$form_state->disableRedirect(); // No page reload
// Return AjaxResponse
}
Redirect Patterns
// Route-based (preferred)
$form_state->setRedirect('route.name', ['param' => $value]);
// URL object
$url = Url::fromRoute('entity.node.canonical', ['node' => 123]);
$form_state->setRedirectUrl($url);
// External URL (rare)
$url = Url::fromUri('https://example.com');
$form_state->setRedirectUrl($url);
// Custom response
$response = new Response('Custom content');
$form_state->setResponse($response);
| Type |
Method |
Persistence |
Use Case |
| Temporary |
setTemporaryValue() |
Single request |
UI state |
| Persistent |
set()/get() |
Across rebuilds |
Multi-step data |
| Values |
getValue() |
Current submission |
Form input |
Rebuild vs Submit
// Rebuild (prevents submit handlers from running)
$form_state->setRebuild(TRUE); // Form rebuilds with new values
// Use rebuild when:
// - User selecting value that changes form
// - AJAX interaction updating fields
// Use submit when:
// - User completing final action
// - AJAX submitting final data (use disableRedirect)
Common Mistakes
- Wrong: Not redirecting after submit → Right: Always redirect (prevents resubmission)
- Wrong: Using string route name when Url object needed → Right: Use Url::fromRoute()
- Wrong: Setting both redirect and custom response → Right: Response wins, choose one
- Wrong: No disableRedirect() in AJAX submit → Right: AJAX response hijacked by redirect
See Also