FormBuilder::buildForm

public FormBuilder::buildForm($form_id, FormStateInterface &$form_state)

Builds and processes a form for a given form ID.

The form may also be retrieved from the cache if the form was built in a previous page load. The form is then passed on for processing, validation, and submission if there is proper input.

Parameters

\Drupal\Core\Form\FormInterface|string $form_id: The value must be one of the following:

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

Return value

array The rendered form. This function may also perform a redirect and hence may not return at all depending upon the $form_state flags that were set.

Throws

\Drupal\Core\Form\FormAjaxException Thrown when a form is triggered via an AJAX submission. It will be handled by \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber.

\Drupal\Core\Form\EnforcedResponseException Thrown when a form builder returns a response directly, usually a \Symfony\Component\HttpFoundation\RedirectResponse. It will be handled by \Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber.

Overrides FormBuilderInterface::buildForm

See also

self::redirectForm()

File

core/lib/Drupal/Core/Form/FormBuilder.php, line 218

Class

FormBuilder
Provides form building and processing.

Namespace

Drupal\Core\Form

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public function buildForm($form_id, FormStateInterface &$form_state) {
  // Ensure the form ID is prepared.
  $form_id = $this->getFormId($form_id, $form_state);
 
  $request = $this->requestStack->getCurrentRequest();
 
  // Inform $form_state about the request method that's building it, so that
  // it can prevent persisting state changes during HTTP methods for which
  // that is disallowed by HTTP: GET and HEAD.
  $form_state->setRequestMethod($request->getMethod());
 
  // Initialize the form's user input. The user input should include only the
  // input meant to be treated as part of what is submitted to the form, so
  // we base it on the form's method rather than the request's method. For
  // example, when someone does a GET request for
  // /node/add/article?destination=foo, which is a form that expects its
  // submission method to be POST, the user input during the GET request
  // should be initialized to empty rather than to ['destination' => 'foo'].
  $input = $form_state->getUserInput();
  if (!isset($input)) {
    $input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();
    $form_state->setUserInput($input);
  }
 
  if (isset($_SESSION['batch_form_state'])) {
    // We've been redirected here after a batch processing. The form has
    // already been processed, but needs to be rebuilt. See _batch_finished().
    $form_state = $_SESSION['batch_form_state'];
    unset($_SESSION['batch_form_state']);
    return $this->rebuildForm($form_id, $form_state);
  }
 
  // If the incoming input contains a form_build_id, we'll check the cache for
  // a copy of the form in question. If it's there, we don't have to rebuild
  // the form to proceed. In addition, if there is stored form_state data from
  // a previous step, we'll retrieve it so it can be passed on to the form
  // processing code.
  $check_cache = isset($input['form_id']) && $input['form_id'] == $form_id && !empty($input['form_build_id']);
  if ($check_cache) {
    $form = $this->getCache($input['form_build_id'], $form_state);
  }
 
  // If the previous bit of code didn't result in a populated $form object, we
  // are hitting the form for the first time and we need to build it from
  // scratch.
  if (!isset($form)) {
    // If we attempted to serve the form from cache, uncacheable $form_state
    // keys need to be removed after retrieving and preparing the form, except
    // any that were already set prior to retrieving the form.
    if ($check_cache) {
      $form_state_before_retrieval = clone $form_state;
    }
 
    $form = $this->retrieveForm($form_id, $form_state);
    $this->prepareForm($form_id, $form, $form_state);
 
    // self::setCache() removes uncacheable $form_state keys (see properties
    // in \Drupal\Core\Form\FormState) in order for multi-step forms to work
    // properly. This means that form processing logic for single-step forms
    // using $form_state->isCached() may depend on data stored in those keys
    // during self::retrieveForm()/self::prepareForm(), but form processing
    // should not depend on whether the form is cached or not, so $form_state
    // is adjusted to match what it would be after a
    // self::setCache()/self::getCache() sequence. These exceptions are
    // allowed to survive here:
    // - always_process: Does not make sense in conjunction with form caching
    //   in the first place, since passing form_build_id as a GET parameter is
    //   not desired.
    // - temporary: Any assigned data is expected to survives within the same
    //   page request.
    if ($check_cache) {
      $cache_form_state = $form_state->getCacheableArray();
      $cache_form_state['always_process'] = $form_state->getAlwaysProcess();
      $cache_form_state['temporary'] = $form_state->getTemporary();
      $form_state = $form_state_before_retrieval;
      $form_state->setFormState($cache_form_state);
    }
  }
 
  // If this form is an AJAX request, disable all form redirects.
  $request = $this->requestStack->getCurrentRequest();
  if ($ajax_form_request = $request->query->has(static::AJAX_FORM_REQUEST)) {
    $form_state->disableRedirect();
  }
 
  // Now that we have a constructed form, process it. This is where:
  // - Element #process functions get called to further refine $form.
  // - User input, if any, gets incorporated in the #value property of the
  //   corresponding elements and into $form_state->getValues().
  // - Validation and submission handlers are called.
  // - If this submission is part of a multistep workflow, the form is rebuilt
  //   to contain the information of the next step.
  // - If necessary, the form and form state are cached or re-cached, so that
  //   appropriate information persists to the next page request.
  // All of the handlers in the pipeline receive $form_state by reference and
  // can use it to know or update information about the state of the form.
  $response = $this->processForm($form_id, $form, $form_state);
 
  // In case the post request exceeds the configured allowed size
  // (post_max_size), the post request is potentially broken. Add some
  // protection against that and at the same time have a nice error message.
  if ($ajax_form_request && !$request->request->has('form_id')) {
    throw new BrokenPostRequestException($this->getFileUploadMaxSize());
  }
 
  // After processing the form, if this is an AJAX form request, interrupt
  // form rendering and return by throwing an exception that contains the
  // processed form and form state. This exception will be caught by
  // \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber::onException() and
  // then passed through
  // \Drupal\Core\Form\FormAjaxResponseBuilderInterface::buildResponse() to
  // build a proper AJAX response.
  // Only do this when the form ID matches, since there is no guarantee from
  // $ajax_form_request that it's an AJAX request for this particular form.
  if ($ajax_form_request && $form_state->isProcessingInput() && $request->request->get('form_id') == $form_id) {
    throw new FormAjaxException($form, $form_state);
  }
 
  // If the form returns a response, skip subsequent page construction by
  // throwing an exception.
  // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
  //
  // @todo Exceptions should not be used for code flow control. However, the
  //   Form API does not integrate with the HTTP Kernel based architecture of
  //   Drupal 8. In order to resolve this issue properly it is necessary to
  //   completely separate form submission from rendering.
  if ($response instanceof Response) {
    throw new EnforcedResponseException($response);
  }
 
  // If this was a successful submission of a single-step form or the last
  // step of a multi-step form, then self::processForm() issued a redirect to
  // another page, or back to this page, but as a new request. Therefore, if
  // we're here, it means that this is either a form being viewed initially
  // before any user input, or there was a validation error requiring the form
  // to be re-displayed, or we're in a multi-step workflow and need to display
  // the form's next step. In any case, we have what we need in $form, and can
  // return it for rendering.
  return $form;
}
doc_Drupal
2025-01-10 15:47:30
Comments
Leave a Comment

Please login to continue.