FormSubmitter.php 4.99 KB
Newer Older
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
<?php

namespace Drupal\Core\Form;

use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Routing\UrlGeneratorInterface;

/**
 * Provides submission processing for forms.
 */
class FormSubmitter implements FormSubmitterInterface {

  /**
   * The URL generator.
   *
   * @var \Drupal\Core\Routing\UrlGeneratorInterface
   */
  protected $urlGenerator;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs a new FormValidator.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
   */
  public function __construct(RequestStack $request_stack, UrlGeneratorInterface $url_generator) {
    $this->requestStack = $request_stack;
    $this->urlGenerator = $url_generator;
  }

  /**
   * {@inheritdoc}
   */
45
  public function doSubmitForm(&$form, FormStateInterface &$form_state) {
46
    if (!$form_state->isSubmitted()) {
47 48 49 50 51 52 53 54 55 56 57 58
      return;
    }

    // Execute form submit handlers.
    $this->executeSubmitHandlers($form, $form_state);

    // If batches were set in the submit handlers, we process them now,
    // possibly ending execution. We make sure we do not react to the batch
    // that is already being processed (if a batch operation performs a
    // \Drupal\Core\Form\FormBuilderInterface::submitForm).
    if ($batch = &$this->batchGet() && !isset($batch['current_set'])) {
      // Store $form_state information in the batch definition.
59
      $batch['form_state'] = $form_state;
60

61
      $batch['progressive'] = !$form_state->isProgrammed();
62 63 64 65 66 67 68 69 70 71 72
      $response = batch_process();
      if ($batch['progressive']) {
        return $response;
      }

      // Execution continues only for programmatic forms.
      // For 'regular' forms, we get redirected to the batch processing
      // page. Form redirection will be handled in _batch_finished(),
      // after the batch is processed.
    }

73
    // Set a flag to indicate the form has been processed and executed.
74
    $form_state->setExecuted();
75 76

    // If no response has been set, process the form redirect.
77
    if (!$form_state->getResponse() && $redirect = $this->redirectForm($form_state)) {
78
      $form_state->setResponse($redirect);
79 80 81
    }

    // If there is a response was set, return it instead of continuing.
82
    if (($response = $form_state->getResponse()) && $response instanceof Response) {
83
      return $response;
84 85 86 87 88 89
    }
  }

  /**
   * {@inheritdoc}
   */
90
  public function executeSubmitHandlers(&$form, FormStateInterface &$form_state) {
91
    // If there was a button pressed, use its handlers.
92
    $handlers = $form_state->getSubmitHandlers();
93
    // Otherwise, check for a form-level handler.
94
    if (!$handlers && !empty($form['#submit'])) {
95 96 97
      $handlers = $form['#submit'];
    }

98
    foreach ($handlers as $callback) {
99 100 101 102 103 104 105 106
      // Check if a previous _submit handler has set a batch, but make sure we
      // do not react to a batch that is already being processed (for instance
      // if a batch operation performs a
      //  \Drupal\Core\Form\FormBuilderInterface::submitForm()).
      if (($batch = &$this->batchGet()) && !isset($batch['id'])) {
        // Some previous submit handler has set a batch. To ensure correct
        // execution order, store the call in a special 'control' batch set.
        // See _batch_next_set().
107
        $batch['sets'][] = ['form_submit' => $callback];
108 109 110
        $batch['has_form_submits'] = TRUE;
      }
      else {
111
        call_user_func_array($form_state->prepareCallback($callback), [&$form, &$form_state]);
112 113 114 115 116 117 118
      }
    }
  }

  /**
   * {@inheritdoc}
   */
119 120
  public function redirectForm(FormStateInterface $form_state) {
    $redirect = $form_state->getRedirect();
121 122

    // Allow using redirect responses directly if needed.
123 124
    if ($redirect instanceof RedirectResponse) {
      return $redirect;
125 126
    }

127
    $url = NULL;
128
    // Check for a route-based redirection.
129
    if ($redirect instanceof Url) {
130
      $url = $redirect->setAbsolute()->toString();
131 132 133
    }
    // If no redirect was specified, redirect to the current path.
    elseif ($redirect === NULL) {
134
      $request = $this->requestStack->getCurrentRequest();
135
      $url = $this->urlGenerator->generateFromRoute('<current>', [], ['query' => $request->query->all(), 'absolute' => TRUE]);
136 137 138
    }

    if ($url) {
139 140 141 142
      // According to RFC 7231, 303 See Other status code must be used to redirect
      // user agent (and not default 302 Found).
      // @see http://tools.ietf.org/html/rfc7231#section-6.4.4
      return new RedirectResponse($url, Response::HTTP_SEE_OTHER);
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    }
  }

  /**
   * Wraps drupal_installation_attempted().
   *
   * @return bool
   */
  protected function drupalInstallationAttempted() {
    return drupal_installation_attempted();
  }

  /**
   * Wraps batch_get().
   */
  protected function &batchGet() {
    return batch_get();
  }

}