Commit 7eb616f1 authored by alexpott's avatar alexpott

Issue #2502785 by dawehner, effulgentsia, tim.plunkett, amateescu, Fabianx,...

Issue #2502785 by dawehner, effulgentsia, tim.plunkett, amateescu, Fabianx, Wim Leers, catch, dsnopek, EclipseGc, yched, Berdir, larowlan, mondrake, olli: Remove support for $form_state->setCached() for GET requests
parent 9d2f34ff
...@@ -2054,12 +2054,6 @@ function hook_validation_constraint_alter(array &$definitions) { ...@@ -2054,12 +2054,6 @@ function hook_validation_constraint_alter(array &$definitions) {
* an array. Here are the details of its elements, all of which are optional: * an array. Here are the details of its elements, all of which are optional:
* - callback: The callback to invoke to handle the server side of the * - callback: The callback to invoke to handle the server side of the
* Ajax event. More information on callbacks is below in @ref sub_callback. * Ajax event. More information on callbacks is below in @ref sub_callback.
* - path: The URL path to use for the request. If omitted, defaults to
* 'system/ajax', which invokes the default Drupal Ajax processing (this will
* call the callback supplied in the 'callback' element). If you supply a
* path, you must set up a routing entry to handle the request yourself and
* return output described in @ref sub_callback below. See the
* @link menu Routing topic @endlink for more information on routing.
* - wrapper: The HTML 'id' attribute of the area where the content returned by * - wrapper: The HTML 'id' attribute of the area where the content returned by
* the callback should be placed. Note that callbacks have a choice of * the callback should be placed. Note that callbacks have a choice of
* returning content or JavaScript commands; 'wrapper' is used for content * returning content or JavaScript commands; 'wrapper' is used for content
...@@ -2085,6 +2079,13 @@ function hook_validation_constraint_alter(array &$definitions) { ...@@ -2085,6 +2079,13 @@ function hook_validation_constraint_alter(array &$definitions) {
* - message: Translated message to display. * - message: Translated message to display.
* - url: For a bar progress indicator, URL path for determining progress. * - url: For a bar progress indicator, URL path for determining progress.
* - interval: For a bar progress indicator, how often to update it. * - interval: For a bar progress indicator, how often to update it.
* - url: A \Drupal\Core\Url to which to submit the Ajax request. If omitted,
* defaults to either the same URL as the form or link destination is for
* someone with JavaScript disabled, or a slightly modified version (e.g.,
* with a query parameter added, removed, or changed) of that URL if
* necessary to support Drupal's content negotiation. It is recommended to
* omit this key and use Drupal's content negotiation rather than using
* substantially different URLs between Ajax and non-Ajax.
* *
* @subsection sub_callback Setting up a callback to process Ajax * @subsection sub_callback Setting up a callback to process Ajax
* Once you have set up your form to trigger an Ajax response (see @ref sub_form * Once you have set up your form to trigger an Ajax response (see @ref sub_form
......
...@@ -185,9 +185,22 @@ public function buildForm($form_id, FormStateInterface &$form_state) { ...@@ -185,9 +185,22 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
// Ensure the form ID is prepared. // Ensure the form ID is prepared.
$form_id = $this->getFormId($form_id, $form_state); $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(); $input = $form_state->getUserInput();
if (!isset($input)) { if (!isset($input)) {
$request = $this->requestStack->getCurrentRequest();
$input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all(); $input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();
$form_state->setUserInput($input); $form_state->setUserInput($input);
} }
...@@ -313,8 +326,22 @@ public function buildForm($form_id, FormStateInterface &$form_state) { ...@@ -313,8 +326,22 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
*/ */
public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) { public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) {
$form = $this->retrieveForm($form_id, $form_state); $form = $this->retrieveForm($form_id, $form_state);
// All rebuilt forms will be cached.
$form_state->setCached(); // 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();
}
// If only parts of the form will be returned to the browser (e.g., Ajax or // 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 // RIA clients), or if the form already had a new build ID regenerated when
......
...@@ -108,9 +108,7 @@ public function buildForm($form_id, FormStateInterface &$form_state); ...@@ -108,9 +108,7 @@ public function buildForm($form_id, FormStateInterface &$form_state);
* form workflow, to be returned for rendering. * form workflow, to be returned for rendering.
* *
* Ajax form submissions are almost always multi-step workflows, so that is * Ajax form submissions are almost always multi-step workflows, so that is
* one common use-case during which form rebuilding occurs. See * one common use-case during which form rebuilding occurs.
* Drupal\system\FormAjaxController::content() for more information about
* creating Ajax-enabled forms.
* *
* @param string $form_id * @param string $form_id
* The unique string identifying the desired form. If a function with that * The unique string identifying the desired form. If a function with that
...@@ -130,7 +128,6 @@ public function buildForm($form_id, FormStateInterface &$form_state); ...@@ -130,7 +128,6 @@ public function buildForm($form_id, FormStateInterface &$form_state);
* The newly built form. * The newly built form.
* *
* @see self::processForm() * @see self::processForm()
* @see \Drupal\system\FormAjaxController::content()
*/ */
public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL); public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL);
......
...@@ -141,16 +141,29 @@ class FormState implements FormStateInterface { ...@@ -141,16 +141,29 @@ class FormState implements FormStateInterface {
/** /**
* The HTTP form method to use for finding the input for this form. * The HTTP form method to use for finding the input for this form.
* *
* May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method forms do * May be 'POST' or 'GET'. Defaults to 'POST'. Note that 'GET' method forms do
* not use form ids so are always considered to be submitted, which can have * not use form ids so are always considered to be submitted, which can have
* unexpected effects. The 'get' method should only be used on forms that do * unexpected effects. The 'GET' method should only be used on forms that do
* not change data, as that is exclusively the domain of 'post.' * not change data, as that is exclusively the domain of 'POST.'
* *
* This property is uncacheable. * This property is uncacheable.
* *
* @var string * @var string
*/ */
protected $method = 'post'; protected $method = 'POST';
/**
* The HTTP method used by the request building or processing this form.
*
* May be any valid HTTP method. Defaults to 'GET', because even though
* $method is 'POST' for most forms, the form's initial build is usually
* performed as part of a GET request.
*
* This property is uncacheable.
*
* @var string
*/
protected $requestMethod = 'GET';
/** /**
* If set to TRUE the original, unprocessed form structure will be cached, * If set to TRUE the original, unprocessed form structure will be cached,
...@@ -475,6 +488,12 @@ public function getButtons() { ...@@ -475,6 +488,12 @@ public function getButtons() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setCached($cache = TRUE) { public function setCached($cache = TRUE) {
// Persisting $form_state is a side-effect disallowed during a "safe" HTTP
// method.
if ($cache && $this->isRequestMethodSafe()) {
throw new \LogicException(sprintf('Form state caching on %s requests is not allowed.', $this->requestMethod));
}
$this->cache = (bool) $cache; $this->cache = (bool) $cache;
return $this; return $this;
} }
...@@ -569,6 +588,29 @@ public function isMethodType($method_type) { ...@@ -569,6 +588,29 @@ public function isMethodType($method_type) {
return $this->method === strtoupper($method_type); return $this->method === strtoupper($method_type);
} }
/**
* {@inheritdoc}
*/
public function setRequestMethod($method) {
$this->requestMethod = strtoupper($method);
return $this;
}
/**
* Checks whether the request method is a "safe" HTTP method.
*
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 defines
* GET and HEAD as "safe" methods, meaning they SHOULD NOT have side-effects,
* such as persisting $form_state changes.
*
* @return bool
*
* @see \Symfony\Component\HttpFoundation\Request::isMethodSafe()
*/
protected function isRequestMethodSafe() {
return in_array($this->requestMethod, array('GET', 'HEAD'));
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -640,6 +640,10 @@ public function getButtons(); ...@@ -640,6 +640,10 @@ public function getButtons();
* TRUE if the form should be cached, FALSE otherwise. * TRUE if the form should be cached, FALSE otherwise.
* *
* @return $this * @return $this
*
* @throws \LogicException
* If the current request is using an HTTP method that must not change
* state (e.g., GET).
*/ */
public function setCached($cache = TRUE); public function setCached($cache = TRUE);
...@@ -731,17 +735,36 @@ public function setLimitValidationErrors($limit_validation_errors); ...@@ -731,17 +735,36 @@ public function setLimitValidationErrors($limit_validation_errors);
public function getLimitValidationErrors(); public function getLimitValidationErrors();
/** /**
* Sets the HTTP form method. * Sets the HTTP method to use for the form's submission.
*
* This is what the form's "method" attribute should be, not necessarily what
* the current request's HTTP method is. For example, a form can have a
* method attribute of POST, but the request that initially builds it uses
* GET.
* *
* @param string $method * @param string $method
* The HTTP form method. * Either "GET" or "POST". Other HTTP methods are not valid form submission
* methods.
* *
* @see \Drupal\Core\Form\FormState::$method * @see \Drupal\Core\Form\FormState::$method
* @see \Drupal\Core\Form\FormStateInterface::setRequestMethod()
* *
* @return $this * @return $this
*/ */
public function setMethod($method); public function setMethod($method);
/**
* Sets the HTTP method used by the request that is building the form.
*
* @param string $method
* Can be any valid HTTP method, such as GET, POST, HEAD, etc.
*
* @return $this
*
* @see \Drupal\Core\Form\FormStateInterface::setMethod()
*/
public function setRequestMethod($method);
/** /**
* Returns the HTTP form method. * Returns the HTTP form method.
* *
......
...@@ -128,14 +128,7 @@ public static function preRenderGroup($element) { ...@@ -128,14 +128,7 @@ public static function preRenderGroup($element) {
* @see self::preRenderAjaxForm() * @see self::preRenderAjaxForm()
*/ */
public static function processAjaxForm(&$element, FormStateInterface $form_state, &$complete_form) { public static function processAjaxForm(&$element, FormStateInterface $form_state, &$complete_form) {
$element = static::preRenderAjaxForm($element); return static::preRenderAjaxForm($element);
// If the element was processed as an #ajax element, and a custom URL was
// provided, set the form to be cached.
if (!empty($element['#ajax_processed']) && !empty($element['#ajax']['url'])) {
$form_state->setCached();
}
return $element;
} }
/** /**
...@@ -238,10 +231,6 @@ public static function preRenderAjaxForm($element) { ...@@ -238,10 +231,6 @@ public static function preRenderAjaxForm($element) {
// content negotiation takes care of formatting the response appropriately. // content negotiation takes care of formatting the response appropriately.
// However, 'url' and 'options' may be set when wanting server processing // However, 'url' and 'options' may be set when wanting server processing
// to be substantially different for a JavaScript triggered submission. // to be substantially different for a JavaScript triggered submission.
// One such substantial difference is form elements that use
// #ajax['callback'] for determining which part of the form needs
// re-rendering. For that, we have a special 'system.ajax' route which
// must be manually set.
$settings += [ $settings += [
'url' => NULL, 'url' => NULL,
'options' => ['query' => []], 'options' => ['query' => []],
......
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
/** /**
* Defines a theme negotiator that deals with the active theme on ajax requests. * Defines a theme negotiator that deals with the active theme on ajax requests.
* *
* Many different pages can invoke an Ajax request to system/ajax or another * Many different pages can invoke an Ajax request to a generic Ajax path. It is
* generic Ajax path. It is almost always desired for an Ajax response to be * almost always desired for an Ajax response to be rendered using the same
* rendered using the same theme as the base page, because most themes are built * theme as the base page, because most themes are built with the assumption
* with the assumption that they control the entire page, so if the CSS for two * that they control the entire page, so if the CSS for two themes are both
* themes are both loaded for a given page, they may conflict with each other. * loaded for a given page, they may conflict with each other. For example,
* For example, Bartik is Drupal's default theme, and Seven is Drupal's default * Bartik is Drupal's default theme, and Seven is Drupal's default
* administration theme. Depending on whether the "Use the administration theme * administration theme. Depending on whether the "Use the administration theme
* when editing or creating content" checkbox is checked, the node edit form may * when editing or creating content" checkbox is checked, the node edit form may
* be displayed in either theme, but the Ajax response to the Field module's * be displayed in either theme, but the Ajax response to the Field module's
......
...@@ -99,6 +99,9 @@ protected function setUp() { ...@@ -99,6 +99,9 @@ protected function setUp() {
*/ */
public function testLoggerSerialization() { public function testLoggerSerialization() {
$form_state = new FormState(); $form_state = new FormState();
// Forms are only serialized during POST requests.
$form_state->setRequestMethod('POST');
$form_state->setCached(); $form_state->setCached();
$form_builder = $this->container->get('form_builder'); $form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($this, $form_state); $form_id = $form_builder->getFormId($this, $form_state);
......
...@@ -7,13 +7,12 @@ ...@@ -7,13 +7,12 @@
namespace Drupal\file\Controller; namespace Drupal\file\Controller;
use Drupal\system\Controller\FormAjaxController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
/** /**
* Defines a controller to respond to file widget AJAX requests. * Defines a controller to respond to file widget AJAX requests.
*/ */
class FileWidgetAjaxController extends FormAjaxController { class FileWidgetAjaxController {
/** /**
* Returns the progress status for a file upload process. * Returns the progress status for a file upload process.
......
...@@ -1591,17 +1591,14 @@ protected function drupalGetAjax($path, array $options = array(), array $headers ...@@ -1591,17 +1591,14 @@ protected function drupalGetAjax($path, array $options = array(), array $headers
* *
* This function can also be called to emulate an Ajax submission. In this * This function can also be called to emulate an Ajax submission. In this
* case, this value needs to be an array with the following keys: * case, this value needs to be an array with the following keys:
* - path: A path to submit the form values to for Ajax-specific processing, * - path: A path to submit the form values to for Ajax-specific processing.
* which is likely different than the $path parameter used for retrieving * - triggering_element: If the value for the 'path' key is a generic Ajax
* the initial form. Defaults to 'system/ajax'. * processing path, this needs to be set to the name of the element. If
* - triggering_element: If the value for the 'path' key is 'system/ajax' or * the name doesn't identify the element uniquely, then this should
* another generic Ajax processing path, this needs to be set to the name * instead be an array with a single key/value pair, corresponding to the
* of the element. If the name doesn't identify the element uniquely, then * element name and value. The \Drupal\Core\Form\FormAjaxResponseBuilder
* this should instead be an array with a single key/value pair, * uses this to find the #ajax information for the element, including
* corresponding to the element name and value. The callback for the * which specific callback to use for processing the request.
* generic Ajax processing path uses this to find the #ajax information
* for the element, including which specific callback to use for
* processing the request.
* *
* This can also be set to NULL in order to emulate an Internet Explorer * This can also be set to NULL in order to emulate an Internet Explorer
* submission of a form with a single text field, and pressing ENTER in that * submission of a form with a single text field, and pressing ENTER in that
...@@ -1649,7 +1646,10 @@ protected function drupalPostForm($path, $edit, $submit, array $options = array( ...@@ -1649,7 +1646,10 @@ protected function drupalPostForm($path, $edit, $submit, array $options = array(
$submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form); $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form);
$action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl();
if ($ajax) { if ($ajax) {
$action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); if (empty($submit['path'])) {
throw new \Exception('No #ajax path specified.');
}
$action = $this->getAbsoluteUrl($submit['path']);
// Ajax callbacks verify the triggering element if necessary, so while // Ajax callbacks verify the triggering element if necessary, so while
// we may eventually want extra code that verifies it in the // we may eventually want extra code that verifies it in the
// handleForm() function, it's not currently a requirement. // handleForm() function, it's not currently a requirement.
...@@ -1735,8 +1735,7 @@ protected function drupalPostForm($path, $edit, $submit, array $options = array( ...@@ -1735,8 +1735,7 @@ protected function drupalPostForm($path, $edit, $submit, array $options = array(
* and the value is the button label. i.e.) array('op' => t('Refresh')). * and the value is the button label. i.e.) array('op' => t('Refresh')).
* @param $ajax_path * @param $ajax_path
* (optional) Override the path set by the Ajax settings of the triggering * (optional) Override the path set by the Ajax settings of the triggering
* element. In the absence of both the triggering element's Ajax path and * element.
* $ajax_path 'system/ajax' will be used.
* @param $options * @param $options
* (optional) Options to be forwarded to the url generator. * (optional) Options to be forwarded to the url generator.
* @param $headers * @param $headers
...@@ -1807,7 +1806,7 @@ protected function drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_p ...@@ -1807,7 +1806,7 @@ protected function drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_p
$extra_post = '&' . $this->serializePostValues($extra_post); $extra_post = '&' . $this->serializePostValues($extra_post);
// Unless a particular path is specified, use the one specified by the // Unless a particular path is specified, use the one specified by the
// Ajax settings, or else 'system/ajax'. // Ajax settings.
if (!isset($ajax_path)) { if (!isset($ajax_path)) {
if (isset($ajax_settings['url'])) { if (isset($ajax_settings['url'])) {
// In order to allow to set for example the wrapper envelope query // In order to allow to set for example the wrapper envelope query
...@@ -1824,10 +1823,12 @@ protected function drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_p ...@@ -1824,10 +1823,12 @@ protected function drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_p
$parsed_url['path'] $parsed_url['path']
); );
} }
else {
$ajax_path = 'system/ajax';
}
} }
if (empty($ajax_path)) {
throw new \Exception('No #ajax path specified.');
}
$ajax_path = $this->container->get('unrouted_url_assembler')->assemble('base://' . $ajax_path, $options); $ajax_path = $this->container->get('unrouted_url_assembler')->assemble('base://' . $ajax_path, $options);
// Submit the POST request. // Submit the POST request.
......
<?php
/**
* @file
* Contains \Drupal\system\Controller\FormAjaxController.
*/
namespace Drupal\system\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Render\MainContent\MainContentRendererInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\system\FileAjaxForm;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* Defines a controller to respond to form Ajax requests.
*/
class FormAjaxController implements ContainerInjectionInterface {
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface|\Drupal\Core\Form\FormCacheInterface
*/
protected $formBuilder;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The main content to AJAX Response renderer.
*
* @var \Drupal\Core\Render\MainContent\MainContentRendererInterface
*/
protected $ajaxRenderer;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The form AJAX response builder.
*
* @var \Drupal\Core\Form\FormAjaxResponseBuilderInterface
*/
protected $formAjaxResponseBuilder;
/**
* Constructs a FormAjaxController object.
*
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
* @param \Drupal\Core\Render\MainContent\MainContentRendererInterface $ajax_renderer
* The main content to AJAX Response renderer.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param \Drupal\Core\Form\FormAjaxResponseBuilderInterface $form_ajax_response_builder
* The form AJAX response builder.
*/
public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder, RendererInterface $renderer, MainContentRendererInterface $ajax_renderer, RouteMatchInterface $route_match, FormAjaxResponseBuilderInterface $form_ajax_response_builder) {
$this->logger = $logger;
$this->formBuilder = $form_builder;
$this->renderer = $renderer;
$this->ajaxRenderer = $ajax_renderer;
$this->routeMatch = $route_match;
$this->formAjaxResponseBuilder = $form_ajax_response_builder;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('logger.factory')->get('ajax'),
$container->get('form_builder'),
$container->get('renderer'),
$container->get('main_content_renderer.ajax'),
$container->get('current_route_match'),
$container->get('form_ajax_response_builder')
);
}
/**
* Processes an Ajax form submission.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return mixed
* Whatever is returned by the triggering element's #ajax['callback']
* function. One of:
* - A render array containing the new or updated content to return to the
* browser. This is commonly an element within the rebuilt form.
* - A \Drupal\Core\Ajax\AjaxResponse object containing commands for the
* browser to process.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
*/
public function content(Request $request) {
$ajax_form = $this->getForm($request);
$form = $ajax_form->getForm();
$form_state = $ajax_form->getFormState();
$commands = $ajax_form->getCommands();
$this->formBuilder->processForm($form['#form_id'], $form, $form_state);
return $this->formAjaxResponseBuilder->buildResponse($request, $form, $form_state, $commands);
}
/**
* Gets a form submitted via #ajax during an Ajax callback.
*
* This will load a form from the form cache used during Ajax operations. It
* pulls the form info from the request body.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return \Drupal\system\FileAjaxForm
* A wrapper object containing the $form, $form_state, $form_id,
* $form_build_id and an initial list of Ajax $commands.
*