class FormAjaxSubscriber

Same name and namespace in other branches
  1. 8.9.x core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
  2. 10 core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
  3. 11.x core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber

Wraps AJAX form submissions that are triggered via an exception.

Hierarchy

Expanded class hierarchy of FormAjaxSubscriber

1 file declares its use of FormAjaxSubscriber
FormAjaxSubscriberTest.php in core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
1 string reference to 'FormAjaxSubscriber'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses FormAjaxSubscriber
form_ajax_subscriber in core/core.services.yml
Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber

File

core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php, line 23

Namespace

Drupal\Core\Form\EventSubscriber
View source
class FormAjaxSubscriber implements EventSubscriberInterface {
    use StringTranslationTrait;
    
    /**
     * The form AJAX response builder.
     *
     * @var \Drupal\Core\Form\FormAjaxResponseBuilderInterface
     */
    protected $formAjaxResponseBuilder;
    
    /**
     * The messenger.
     *
     * @var \Drupal\Core\Messenger\MessengerInterface
     */
    protected $messenger;
    
    /**
     * Constructs a new FormAjaxSubscriber.
     *
     * @param \Drupal\Core\Form\FormAjaxResponseBuilderInterface $form_ajax_response_builder
     *   The form AJAX response builder.
     * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
     *   The string translation.
     * @param \Drupal\Core\Messenger\MessengerInterface $messenger
     *   The messenger.
     */
    public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder, TranslationInterface $string_translation, MessengerInterface $messenger) {
        $this->formAjaxResponseBuilder = $form_ajax_response_builder;
        $this->stringTranslation = $string_translation;
        $this->messenger = $messenger;
    }
    
    /**
     * Alters the wrapper format if this is an AJAX form request.
     *
     * @param \Symfony\Component\HttpKernel\Event\ViewEvent $event
     *   The event to process.
     */
    public function onView(ViewEvent $event) {
        // To support an AJAX form submission of a form within a block, make the
        // later VIEW subscribers process the controller result as though for
        // HTML display (i.e., add blocks). During that block building, when the
        // submitted form gets processed, an exception gets thrown by
        // \Drupal\Core\Form\FormBuilderInterface::buildForm(), allowing
        // self::onException() to return an AJAX response instead of an HTML one.
        $request = $event->getRequest();
        if ($request->query
            ->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
            $request->query
                ->set(MainContentViewSubscriber::WRAPPER_FORMAT, 'html');
        }
    }
    
    /**
     * Catches a form AJAX exception and build a response from it.
     *
     * @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
     *   The event to process.
     */
    public function onException(ExceptionEvent $event) {
        $exception = $event->getThrowable();
        $request = $event->getRequest();
        // Render a nice error message in case we have a file upload which exceeds
        // the configured upload limit.
        if ($exception instanceof BrokenPostRequestException && $request->query
            ->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
            $this->messenger
                ->addError($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', [
                '@size' => $this->formatSize($exception->getSize()),
            ]));
            $response = new AjaxResponse(NULL, 200);
            $status_messages = [
                '#type' => 'status_messages',
            ];
            $response->addCommand(new PrependCommand(NULL, $status_messages));
            $event->allowCustomResponseCode();
            $event->setResponse($response);
            return;
        }
        // Extract the form AJAX exception (it may have been passed to another
        // exception before reaching here).
        if ($exception = $this->getFormAjaxException($exception)) {
            $request = $event->getRequest();
            $form = $exception->getForm();
            $form_state = $exception->getFormState();
            // Set the build ID from the request as the old build ID on the form.
            $form['#build_id_old'] = $request->request
                ->get('form_build_id');
            try {
                $response = $this->formAjaxResponseBuilder
                    ->buildResponse($request, $form, $form_state, []);
                // Since this response is being set in place of an exception, explicitly
                // mark this as a 200 status.
                $response->setStatusCode(200);
                $event->allowCustomResponseCode();
                $event->setResponse($response);
            } catch (\Exception $e) {
                // Otherwise, replace the existing exception with the new one.
                $event->setThrowable($e);
            }
        }
    }
    
    /**
     * Extracts a form AJAX exception.
     *
     * @param \Exception $e
     *   A generic exception that might contain a form AJAX exception.
     *
     * @return \Drupal\Core\Form\FormAjaxException|null
     *   Either the form AJAX exception, or NULL if none could be found.
     */
    protected function getFormAjaxException(\Exception $e) {
        $exception = NULL;
        while ($e) {
            if ($e instanceof FormAjaxException) {
                $exception = $e;
                break;
            }
            $e = $e->getPrevious();
        }
        return $exception;
    }
    
    /**
     * Wraps format_size()
     *
     * @return string
     *   The formatted size.
     */
    protected function formatSize($size) {
        return format_size($size);
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
        // Run before exception.logger.
        $events[KernelEvents::EXCEPTION] = [
            'onException',
            51,
        ];
        // Run before main_content_view_subscriber.
        $events[KernelEvents::VIEW][] = [
            'onView',
            1,
        ];
        return $events;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overrides
FormAjaxSubscriber::$formAjaxResponseBuilder protected property The form AJAX response builder.
FormAjaxSubscriber::$messenger protected property The messenger.
FormAjaxSubscriber::formatSize protected function Wraps format_size()
FormAjaxSubscriber::getFormAjaxException protected function Extracts a form AJAX exception.
FormAjaxSubscriber::getSubscribedEvents public static function
FormAjaxSubscriber::onException public function Catches a form AJAX exception and build a response from it.
FormAjaxSubscriber::onView public function Alters the wrapper format if this is an AJAX form request.
FormAjaxSubscriber::__construct public function Constructs a new FormAjaxSubscriber.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.