public function FormBuilder::rebuildForm in Drupal 9
Same name and namespace in other branches
- 8 core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::rebuildForm()
Constructs a new $form from the information in $form_state.
This is the key function for making multi-step forms advance from step to step. It is called by self::processForm() when all user input processing, including calling validation and submission handlers, for the request is finished. If a validate or submit handler set $form_state->isRebuilding() to TRUE, and if other conditions don't preempt a rebuild from happening, then this function is called to generate a new $form, the next step in the form workflow, to be returned for rendering.
Ajax form submissions are almost always multi-step workflows, so that is one common use-case during which form rebuilding occurs.
Parameters
string $form_id: The unique string identifying the desired form. If a function with that name exists, it is called to build the form array.
\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.
array|null $old_form: (optional) A previously built $form. Used to retain the #build_id and #action properties in Ajax callbacks and similar partial form rebuilds. The only properties copied from $old_form are the ones which both exist in $old_form and for which $form_state->getRebuildInfo()['copy'][PROPERTY] is TRUE. If $old_form is not passed, the entire $form is rebuilt freshly. 'rebuild_info' needs to be a separate top-level property next to 'build_info', since the contained data must not be cached.
Return value
array The newly built form.
Overrides FormBuilderInterface::rebuildForm
See also
2 calls to FormBuilder::rebuildForm()
- FormBuilder::buildForm in core/
lib/ Drupal/ Core/ Form/ FormBuilder.php - Builds and processes a form for a given form ID.
- FormBuilder::processForm in core/
lib/ Drupal/ Core/ Form/ FormBuilder.php - Processes a form submission.
File
- core/
lib/ Drupal/ Core/ Form/ FormBuilder.php, line 369
Class
- FormBuilder
- Provides form building and processing.
Namespace
Drupal\Core\FormCode
public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) {
$form = $this
->retrieveForm($form_id, $form_state);
// Only GET and POST are valid form methods. If the form receives its input
// via POST, then $form_state must be persisted when it is rebuilt between
// submissions. If the form receives its input via GET, then persisting
// state is forbidden by $form_state->setCached(), and the form must use
// the URL itself to transfer its state across steps. Although $form_state
// throws an exception based on the request method rather than the form's
// method, we base the decision to cache on the form method, because:
// - It's the form method that defines what the form needs to do to manage
// its state.
// - rebuildForm() should only be called after successful input processing,
// which means the request method matches the form method, and if not,
// there's some other error, so it's ok if an exception is thrown.
if ($form_state
->isMethodType('POST')) {
$form_state
->setCached();
}
// \Drupal\Component\Utility\Html::getUniqueId() maintains a cache of
// element IDs it has seen, so it can prevent duplicates. We want to be
// sure we reset that cache when a form is processed, so scenarios that
// result in the form being built behind the scenes and again for the
// browser don't increment all the element IDs needlessly.
if (!FormState::hasAnyErrors()) {
// We only reset HTML ID's when there are no validation errors as this can
// cause ID collisions with other forms on the page otherwise.
Html::resetSeenIds();
}
// If only parts of the form will be returned to the browser (e.g., Ajax or
// RIA clients), or if the form already had a new build ID regenerated when
// it was retrieved from the form cache, reuse the existing #build_id.
// Otherwise, a new #build_id is generated, to not clobber the previous
// build's data in the form cache; also allowing the user to go back to an
// earlier build, make changes, and re-submit.
// @see self::prepareForm()
$rebuild_info = $form_state
->getRebuildInfo();
$enforce_old_build_id = isset($old_form['#build_id']) && !empty($rebuild_info['copy']['#build_id']);
$old_form_is_mutable_copy = isset($old_form['#build_id_old']);
if ($enforce_old_build_id || $old_form_is_mutable_copy) {
$form['#build_id'] = $old_form['#build_id'];
if ($old_form_is_mutable_copy) {
$form['#build_id_old'] = $old_form['#build_id_old'];
}
}
else {
if (isset($old_form['#build_id'])) {
$form['#build_id_old'] = $old_form['#build_id'];
}
$form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
}
// #action defaults to $request->getRequestUri(), but in case of Ajax and
// other partial rebuilds, the form is submitted to an alternate URL, and
// the original #action needs to be retained.
if (isset($old_form['#action']) && !empty($rebuild_info['copy']['#action'])) {
$form['#action'] = $old_form['#action'];
}
$this
->prepareForm($form_id, $form, $form_state);
// Caching is normally done in self::processForm(), but what needs to be
// cached is the $form structure before it passes through
// self::doBuildForm(), so we need to do it here.
// @todo For Drupal 8, find a way to avoid this code duplication.
if ($form_state
->isCached()) {
$this
->setCache($form['#build_id'], $form, $form_state);
}
// Clear out all group associations as these might be different when
// re-rendering the form.
$form_state
->setGroups([]);
// Return a fully built form that is ready for rendering.
return $this
->doBuildForm($form_id, $form, $form_state);
}