class ViewsFormBase

Same name and namespace in other branches
  1. 9 core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php \Drupal\views_ui\Form\Ajax\ViewsFormBase
  2. 8.9.x core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php \Drupal\views_ui\Form\Ajax\ViewsFormBase
  3. 11.x core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php \Drupal\views_ui\Form\Ajax\ViewsFormBase

Provides a base class for Views UI AJAX forms.

Hierarchy

Expanded class hierarchy of ViewsFormBase

File

core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php, line 26

Namespace

Drupal\views_ui\Form\Ajax
View source
abstract class ViewsFormBase extends FormBase implements ViewsFormInterface {
  
  /**
   * The ID of the item this form is manipulating.
   *
   * @var string
   */
  protected $id;
  
  /**
   * The type of item this form is manipulating.
   *
   * @var string
   */
  protected $type;
  
  /**
   * Sets the ID for this form.
   *
   * @param string $id
   *   The ID of the item this form is manipulating.
   */
  protected function setID($id) {
    if ($id) {
      $this->id = $id;
    }
  }
  
  /**
   * Sets the type for this form.
   *
   * @param string $type
   *   The type of the item this form is manipulating.
   */
  protected function setType($type) {
    if ($type) {
      $this->type = $type;
    }
  }
  
  /**
   * {@inheritdoc}
   */
  public function getFormState(ViewEntityInterface $view, $display_id, $js) {
    // $js may already have been converted to a Boolean.
    $ajax = is_string($js) ? $js === 'ajax' : $js;
    return (new FormState())->set('form_id', $this->getFormId())
      ->set('form_key', $this->getFormKey())
      ->set('ajax', $ajax)
      ->set('display_id', $display_id)
      ->set('view', $view)
      ->set('type', $this->type)
      ->set('id', $this->id)
      ->disableRedirect()
      ->addBuildInfo('callback_object', $this);
  }
  
  /**
   * {@inheritdoc}
   */
  public function getForm(ViewEntityInterface $view, $display_id, $js) {
    /** @var \Drupal\Core\Form\FormStateInterface $form_state */
    $form_state = $this->getFormState($view, $display_id, $js);
    $view = $form_state->get('view');
    $form_key = $form_state->get('form_key');
    // @todo Remove the need for this.
    \Drupal::moduleHandler()->loadInclude('views_ui', 'inc', 'admin');
    // Reset the cache of IDs. Drupal rather aggressively prevents ID
    // duplication but this causes it to remember IDs that are no longer even
    // being used.
    Html::resetSeenIds();
    // Check to see if this is the top form of the stack. If it is, pop
    // it off; if it isn't, the user clicked somewhere else and the stack is
    // now irrelevant.
    if (!empty($view->stack)) {
      $identifier = implode('-', array_filter([
        $form_key,
        $view->id(),
        $display_id,
        $form_state->get('type'),
        $form_state->get('id'),
      ]));
      // Retrieve the first form from the stack without changing the integer keys,
      // as they're being used for the "2 of 3" progress indicator.
      reset($view->stack);
      $stack_key = key($view->stack);
      $top = current($view->stack);
      next($view->stack);
      unset($view->stack[$stack_key]);
      if (array_shift($top) != $identifier) {
        $view->stack = [];
      }
    }
    // Automatically remove the form cache if it is set and the key does
    // not match. This way navigating away from the form without hitting
    // update will work.
    if (isset($view->form_cache) && $view->form_cache['key'] !== $form_key) {
      unset($view->form_cache);
    }
    $form_class = get_class($form_state->getFormObject());
    $response = $this->ajaxFormWrapper($form_class, $form_state);
    // If the form has not been submitted, or was not set for rerendering, stop.
    if (!$form_state->isSubmitted() || $form_state->get('rerender')) {
      return $response;
    }
    // Sometimes we need to re-generate the form for multi-step type operations.
    if (!empty($view->stack)) {
      $stack = $view->stack;
      $top = array_shift($stack);
      // Build the new form state for the next form in the stack.
      $reflection = new \ReflectionClass($view::$forms[$top[1]]);
      $form_state = $reflection->newInstanceArgs(array_slice($top, 3, 2))
        ->getFormState($view, $top[2], $form_state->get('ajax'));
      $form_class = get_class($form_state->getFormObject());
      $form_state->setUserInput([]);
      $form_url = views_ui_build_form_url($form_state);
      if (!$form_state->get('ajax')) {
        return new RedirectResponse($form_url->setAbsolute()
          ->toString());
      }
      $form_state->set('url', $form_url);
      $response = $this->ajaxFormWrapper($form_class, $form_state);
    }
    elseif (!$form_state->get('ajax')) {
      // If nothing on the stack, non-js forms just go back to the main view editor.
      $display_id = $form_state->get('display_id');
      return new RedirectResponse(Url::fromRoute('entity.view.edit_display_form', [
        'view' => $view->id(),
        'display_id' => $display_id,
      ], [
        'absolute' => TRUE,
      ])
        ->toString());
    }
    else {
      $response = new AjaxResponse();
      $response->addCommand(new CloseModalDialogCommand());
      $response->addCommand(new ShowButtonsCommand(!empty($view->changed)));
      $response->addCommand(new TriggerPreviewCommand());
      if ($page_title = $form_state->get('page_title')) {
        $response->addCommand(new ReplaceTitleCommand($page_title));
      }
    }
    // If this form was for view-wide changes, there's no need to regenerate
    // the display section of the form.
    if ($display_id !== '') {
      \Drupal::entityTypeManager()->getFormObject('view', 'edit')
        ->rebuildCurrentTab($view, $response, $display_id);
    }
    return $response;
  }
  
  /**
   * Wrapper for handling AJAX forms.
   *
   * Wrapper around \Drupal\Core\Form\FormBuilderInterface::buildForm() to
   * handle some AJAX stuff automatically.
   * This makes some assumptions about the client.
   *
   * @param \Drupal\Core\Form\FormInterface|string $form_class
   *   The value must be one of the following:
   *   - The name of a class that implements \Drupal\Core\Form\FormInterface.
   *   - An instance of a class that implements \Drupal\Core\Form\FormInterface.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse|string|array
   *   Returns one of three possible values:
   *   - A \Drupal\Core\Ajax\AjaxResponse object.
   *   - The rendered form, as a string.
   *   - A render array with the title in #title and the rendered form in the
   *   #markup array.
   */
  protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) {
    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    // This won't override settings already in.
    if (!$form_state->has('rerender')) {
      $form_state->set('rerender', FALSE);
    }
    $ajax = $form_state->get('ajax');
    // Do not overwrite if the redirect has been disabled.
    if (!$form_state->isRedirectDisabled()) {
      $form_state->disableRedirect($ajax);
    }
    $form_state->disableCache();
    // Builds the form in a render context in order to ensure that cacheable
    // metadata is bubbled up.
    $render_context = new RenderContext();
    $callable = function () use ($form_class, &$form_state) {
      return \Drupal::formBuilder()->buildForm($form_class, $form_state);
    };
    $form = $renderer->executeInRenderContext($render_context, $callable);
    if (!$render_context->isEmpty()) {
      BubbleableMetadata::createFromRenderArray($form)->merge($render_context->pop())
        ->applyTo($form);
    }
    $output = $renderer->renderRoot($form);
    // These forms have the title built in, so set the title here:
    $title = $form_state->get('title') ?: '';
    if ($ajax && (!$form_state->isExecuted() || $form_state->get('rerender'))) {
      // If the form didn't execute and we're using ajax, build up an
      // Ajax command list to execute.
      $response = new AjaxResponse();
      // Attach the library necessary for using the OpenModalDialogCommand and
      // set the attachments for this Ajax response.
      $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
      $response->setAttachments($form['#attached']);
      $display = '';
      $status_messages = [
        '#type' => 'status_messages',
      ];
      if ($messages = $renderer->renderRoot($status_messages)) {
        $display = '<div class="views-messages">' . $messages . '</div>';
      }
      $display .= $output;
      $options = [
        'classes' => [
          'ui-dialog' => 'views-ui-dialog js-views-ui-dialog',
        ],
        'width' => '75%',
      ];
      $response->addCommand(new OpenModalDialogCommand($title, $display, $options));
      // Views provides its own custom handling of AJAX form submissions.
      // Usually this happens at the same path, but custom paths may be
      // specified in $form_state.
      $form_url = $form_state->has('url') ? $form_state->get('url')
        ->toString() : Url::fromRoute('<current>')->toString();
      $response->addCommand(new SetFormCommand($form_url));
      if ($section = $form_state->get('#section')) {
        $response->addCommand(new HighlightCommand('.' . Html::cleanCssIdentifier($section)));
      }
      return $response;
    }
    return $title ? [
      '#title' => $title,
      '#markup' => $output,
    ] : $output;
  }
  
  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
  }
  
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 3
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 3
FormBase::container private function Returns the service container.
FormBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 111
FormBase::currentUser protected function Gets the current user. 2
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
FormInterface::buildForm public function Form constructor. 203
FormInterface::getFormId public function Returns a unique string identifying the form. 284
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 16
MessengerTrait::messenger public function Gets the messenger. 16
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 2
RedirectDestinationTrait::getDestinationArray protected function Prepares a &#039;destination&#039; URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
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.
ViewsFormBase::$id protected property The ID of the item this form is manipulating.
ViewsFormBase::$type protected property The type of item this form is manipulating.
ViewsFormBase::ajaxFormWrapper protected function Wrapper for handling AJAX forms.
ViewsFormBase::getForm public function Creates a new instance of this form. Overrides ViewsFormInterface::getForm 6
ViewsFormBase::getFormState public function Gets the form state for this form. Overrides ViewsFormInterface::getFormState 1
ViewsFormBase::setID protected function Sets the ID for this form.
ViewsFormBase::setType protected function Sets the type for this form.
ViewsFormBase::submitForm public function Form submission handler. Overrides FormInterface::submitForm 9
ViewsFormBase::validateForm public function Form validation handler. Overrides FormBase::validateForm 3
ViewsFormInterface::getFormKey public function Returns the key that represents this form. 10

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