You are here

abstract class FormWizardBase in Chaos Tool Suite (ctools) 8.3

The base class for all form wizard.

Hierarchy

Expanded class hierarchy of FormWizardBase

1 file declares its use of FormWizardBase
WizardTest.php in tests/modules/ctools_wizard_test/src/Wizard/WizardTest.php

File

src/Wizard/FormWizardBase.php, line 23

Namespace

Drupal\ctools\Wizard
View source
abstract class FormWizardBase extends FormBase implements FormWizardInterface {

  /**
   * Tempstore Factory for keeping track of values in each step of the wizard.
   *
   * @var \Drupal\Core\TempStore\SharedTempStoreFactory
   */
  protected $tempstore;

  /**
   * The Form Builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $builder;

  /**
   * The class resolver.
   *
   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
   */
  protected $classResolver;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $dispatcher;

  /**
   * The shared temp store factory collection name.
   *
   * @var string
   */
  protected $tempstore_id;

  /**
   * The SharedTempStore key for our current wizard values.
   *
   * @var string|null
   */
  protected $machine_name;

  /**
   * The current active step of the wizard.
   *
   * @var string|null
   */
  protected $step;

  /**
   * @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
   *   Tempstore Factory for keeping track of values in each step of the
   *   wizard.
   * @param \Drupal\Core\Form\FormBuilderInterface $builder
   *   The Form Builder.
   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
   *   The class resolver.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
   * @param $tempstore_id
   *   The shared temp store factory collection name.
   * @param null $machine_name
   *   The SharedTempStore key for our current wizard values.
   * @param null $step
   *   The current active step of the wizard.
   */
  public function __construct(SharedTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match, $tempstore_id, $machine_name = NULL, $step = NULL) {
    $this->tempstore = $tempstore;
    $this->builder = $builder;
    $this->classResolver = $class_resolver;
    $this->dispatcher = $event_dispatcher;
    $this->routeMatch = $route_match;
    $this->tempstore_id = $tempstore_id;
    $this->machine_name = $machine_name;
    $this->step = $step;
  }

  /**
   * {@inheritdoc}
   */
  public static function getParameters() {
    return [
      'tempstore' => \Drupal::service('tempstore.shared'),
      'builder' => \Drupal::service('form_builder'),
      'class_resolver' => \Drupal::service('class_resolver'),
      'event_dispatcher' => \Drupal::service('event_dispatcher'),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function initValues() {
    $values = [];
    $event = new WizardEvent($this, $values);
    $this->dispatcher
      ->dispatch(FormWizardInterface::LOAD_VALUES, $event);
    return $event
      ->getValues();
  }

  /**
   * {@inheritdoc}
   */
  public function getTempstoreId() {
    return $this->tempstore_id;
  }

  /**
   * {@inheritdoc}
   */
  public function getTempstore() {
    $tempstore = $this->tempstore
      ->get($this
      ->getTempstoreId());
    return $tempstore;
  }

  /**
   * {@inheritdoc}
   */
  public function getMachineName() {
    return $this->machine_name;
  }

  /**
   * {@inheritdoc}
   */
  public function getStep($cached_values) {
    if (!$this->step) {
      $operations = $this
        ->getOperations($cached_values);
      $steps = array_keys($operations);
      $this->step = reset($steps);
    }
    return $this->step;
  }

  /**
   * {@inheritdoc}
   */
  public function getOperation($cached_values) {
    $operations = $this
      ->getOperations($cached_values);
    $step = $this
      ->getStep($cached_values);
    if (!empty($operations[$step])) {
      return $operations[$step];
    }
    throw new NotFoundHttpException();
  }

  /**
   * The translated text of the "Next" button's text.
   *
   * @return string
   */
  public function getNextOp() {
    return $this
      ->t('Next');
  }

  /**
   * {@inheritdoc}
   */
  public function getNextParameters($cached_values) {

    // Get the steps by key.
    $operations = $this
      ->getOperations($cached_values);
    $steps = array_keys($operations);

    // Get the steps after the current step.
    $after = array_slice($operations, array_search($this
      ->getStep($cached_values), $steps) + 1);

    // Get the steps after the current step by key.
    $after_keys = array_keys($after);
    $step = reset($after_keys);
    if (!$step) {
      $keys = array_keys($operations);
      $step = end($keys);
    }
    return [
      'machine_name' => $this
        ->getMachineName(),
      'step' => $step,
      'js' => 'nojs',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getPreviousParameters($cached_values) {
    $operations = $this
      ->getOperations($cached_values);
    $step = $this
      ->getStep($cached_values);

    // Get the steps by key.
    $steps = array_keys($operations);

    // Get the steps before the current step.
    $before = array_slice($operations, 0, array_search($step, $steps));

    // Get the steps before the current step by key.
    $before = array_keys($before);

    // Reverse the steps for easy access to the next step.
    $before_steps = array_reverse($before);
    $step = reset($before_steps);
    return [
      'machine_name' => $this
        ->getMachineName(),
      'step' => $step,
      'js' => 'nojs',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    if (!$this
      ->getMachineName() || !$this
      ->getTempstore()
      ->get($this
      ->getMachineName())) {
      $cached_values = $this
        ->initValues();
    }
    else {
      $cached_values = $this
        ->getTempstore()
        ->get($this
        ->getMachineName());
    }
    $operation = $this
      ->getOperation($cached_values);

    /* @var $operation \Drupal\Core\Form\FormInterface */
    $operation = $this->classResolver
      ->getInstanceFromDefinition($operation['form']);
    return $operation
      ->getFormId();
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $cached_values = $form_state
      ->getTemporaryValue('wizard');

    // Get the current form operation.
    $operation = $this
      ->getOperation($cached_values);
    $form = $this
      ->customizeForm($form, $form_state);

    /* @var $formClass \Drupal\Core\Form\FormInterface */
    $formClass = $this->classResolver
      ->getInstanceFromDefinition($operation['form']);

    // Pass include any custom values for this operation.
    if (!empty($operation['values'])) {
      $cached_values = array_merge($cached_values, $operation['values']);
      $form_state
        ->setTemporaryValue('wizard', $cached_values);
    }

    // Build the form.
    $form = $formClass
      ->buildForm($form, $form_state);
    if (isset($operation['title'])) {
      $form['#title'] = $operation['title'];
    }
    $form['actions'] = $this
      ->actions($formClass, $form_state);
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    // Only perform this logic if we're moving to the next page. This prevents
    // the loss of cached values on ajax submissions.
    if ((string) $form_state
      ->getValue('op') == (string) $this
      ->getNextOp()) {
      $cached_values = $form_state
        ->getTemporaryValue('wizard');
      if ($form_state
        ->hasValue('label')) {
        $cached_values['label'] = $form_state
          ->getValue('label');
      }
      if ($form_state
        ->hasValue('id')) {
        $cached_values['id'] = $form_state
          ->getValue('id');
      }
      if (is_null($this->machine_name) && !empty($cached_values['id'])) {
        $this->machine_name = $cached_values['id'];
      }
      $this
        ->getTempstore()
        ->set($this
        ->getMachineName(), $cached_values);
      if (!$form_state
        ->get('ajax')) {
        $form_state
          ->setRedirect($this
          ->getRouteName(), $this
          ->getNextParameters($cached_values));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function populateCachedValues(array &$form, FormStateInterface $form_state) {
    $cached_values = $this
      ->getTempstore()
      ->get($this
      ->getMachineName());
    if (!$cached_values) {
      $cached_values = $form_state
        ->getTemporaryValue('wizard');
      if (!$cached_values) {
        $cached_values = $this
          ->initValues();
        $form_state
          ->setTemporaryValue('wizard', $cached_values);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function previous(array &$form, FormStateInterface $form_state) {
    $cached_values = $form_state
      ->getTemporaryValue('wizard');
    $form_state
      ->setRedirect($this
      ->getRouteName(), $this
      ->getPreviousParameters($cached_values));
  }

  /**
   * {@inheritdoc}
   */
  public function finish(array &$form, FormStateInterface $form_state) {
    $this
      ->getTempstore()
      ->delete($this
      ->getMachineName());
  }

  /**
   * Helper function for generating default form elements.
   *
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *
   * @return array
   */
  protected function customizeForm(array $form, FormStateInterface $form_state) {

    // Setup the step rendering theme element.
    $prefix = [
      '#theme' => [
        'ctools_wizard_trail',
      ],
      '#wizard' => $this,
      '#cached_values' => $form_state
        ->getTemporaryValue('wizard'),
    ];

    // @todo properly inject the renderer.
    $form['#prefix'] = \Drupal::service('renderer')
      ->render($prefix);
    return $form;
  }

  /**
   * Generates action elements for navigating between the operation steps.
   *
   * @param \Drupal\Core\Form\FormInterface $form_object
   *   The current operation form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return array
   */
  protected function actions(FormInterface $form_object, FormStateInterface $form_state) {
    $cached_values = $form_state
      ->getTemporaryValue('wizard');
    $operations = $this
      ->getOperations($cached_values);
    $step = $this
      ->getStep($cached_values);
    $operation = $operations[$step];
    $steps = array_keys($operations);

    // Slice to find the operations that occur before the current operation.
    $before = array_slice($operations, 0, array_search($step, $steps));

    // Slice to find the operations that occur after the current operation.
    $after = array_slice($operations, array_search($step, $steps) + 1);
    $actions = [
      'submit' => [
        '#type' => 'submit',
        '#value' => $this
          ->t('Next'),
        '#button_type' => 'primary',
        '#validate' => [
          '::populateCachedValues',
          [
            $form_object,
            'validateForm',
          ],
        ],
        '#submit' => [
          [
            $form_object,
            'submitForm',
          ],
        ],
      ],
    ];

    // Add any submit or validate functions for the step and the global ones.
    if (isset($operation['validate'])) {
      $actions['submit']['#validate'] = array_merge($actions['submit']['#validate'], $operation['validate']);
    }
    $actions['submit']['#validate'][] = '::validateForm';
    if (isset($operation['submit'])) {
      $actions['submit']['#submit'] = array_merge($actions['submit']['#submit'], $operation['submit']);
    }
    $actions['submit']['#submit'][] = '::submitForm';
    if ($form_state
      ->get('ajax')) {

      // Ajax submissions need to submit to the current step, not "next".
      $parameters = $this
        ->getNextParameters($cached_values);
      $parameters['step'] = $this
        ->getStep($cached_values);
      $actions['submit']['#ajax'] = [
        'callback' => '::ajaxSubmit',
        'url' => Url::fromRoute($this
          ->getRouteName(), $parameters),
        'options' => [
          'query' => $this
            ->getRequest()->query
            ->all() + [
            FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
          ],
        ],
      ];
    }

    // If there are steps before this one, label the button "previous"
    // otherwise do not display a button.
    if ($before) {
      $actions['previous'] = [
        '#type' => 'submit',
        '#value' => $this
          ->t('Previous'),
        '#validate' => [
          [
            $this,
            'populateCachedValues',
          ],
        ],
        '#submit' => [
          [
            $this,
            'previous',
          ],
        ],
        '#limit_validation_errors' => [],
        '#weight' => -10,
      ];
      if ($form_state
        ->get('ajax')) {

        // Ajax submissions need to submit to the current step, not "previous".
        $parameters = $this
          ->getPreviousParameters($cached_values);
        $parameters['step'] = $this
          ->getStep($cached_values);
        $actions['previous']['#ajax'] = [
          'callback' => '::ajaxPrevious',
          'url' => Url::fromRoute($this
            ->getRouteName(), $parameters),
          'options' => [
            'query' => $this
              ->getRequest()->query
              ->all() + [
              FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
            ],
          ],
        ];
      }
    }

    // If there are not steps after this one, label the button "Finish".
    if (!$after) {
      $actions['submit']['#value'] = $this
        ->t('Finish');
      $actions['submit']['#submit'][] = [
        $this,
        'finish',
      ];
      if ($form_state
        ->get('ajax')) {
        $actions['submit']['#ajax']['callback'] = [
          $this,
          'ajaxFinish',
        ];
      }
    }
    return $actions;
  }
  public function ajaxSubmit(array $form, FormStateInterface $form_state) {
    $cached_values = $form_state
      ->getTemporaryValue('wizard');
    $response = new AjaxResponse();
    $parameters = $this
      ->getNextParameters($cached_values);
    $response
      ->addCommand(new OpenModalWizardCommand($this, $this
      ->getTempstoreId(), $parameters));
    return $response;
  }
  public function ajaxPrevious(array $form, FormStateInterface $form_state) {
    $cached_values = $form_state
      ->getTemporaryValue('wizard');
    $response = new AjaxResponse();
    $parameters = $this
      ->getPreviousParameters($cached_values);
    $response
      ->addCommand(new OpenModalWizardCommand($this, $this
      ->getTempstoreId(), $parameters));
    return $response;
  }
  public function ajaxFinish(array $form, FormStateInterface $form_state) {
    $response = new AjaxResponse();
    $response
      ->addCommand(new CloseModalDialogCommand());
    return $response;
  }
  public function getRouteName() {
    return $this->routeMatch
      ->getRouteName();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 1
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. 1
FormBase::container private function Returns the service container.
FormBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 87
FormBase::currentUser protected function Gets the current user.
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. Overrides UrlGeneratorTrait::redirect
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.
FormWizardBase::$builder protected property The Form Builder.
FormWizardBase::$classResolver protected property The class resolver.
FormWizardBase::$dispatcher protected property The event dispatcher.
FormWizardBase::$machine_name protected property The SharedTempStore key for our current wizard values.
FormWizardBase::$step protected property The current active step of the wizard.
FormWizardBase::$tempstore protected property Tempstore Factory for keeping track of values in each step of the wizard.
FormWizardBase::$tempstore_id protected property The shared temp store factory collection name.
FormWizardBase::actions protected function Generates action elements for navigating between the operation steps.
FormWizardBase::ajaxFinish public function Overrides FormWizardInterface::ajaxFinish
FormWizardBase::ajaxPrevious public function Overrides FormWizardInterface::ajaxPrevious
FormWizardBase::ajaxSubmit public function Overrides FormWizardInterface::ajaxSubmit
FormWizardBase::buildForm public function Form constructor. Overrides FormInterface::buildForm
FormWizardBase::customizeForm protected function Helper function for generating default form elements. 1
FormWizardBase::finish public function Form submit handler for finalizing the wizard values. Overrides FormWizardInterface::finish 2
FormWizardBase::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
FormWizardBase::getMachineName public function The SharedTempStore key for our current wizard values. Overrides FormWizardInterface::getMachineName
FormWizardBase::getNextOp public function The translated text of the "Next" button's text.
FormWizardBase::getNextParameters public function The Route parameters for a 'next' step. Overrides FormWizardInterface::getNextParameters
FormWizardBase::getOperation public function Retrieve the current Operation. Overrides FormWizardInterface::getOperation
FormWizardBase::getParameters public static function Return an array of parameters required to construct this wizard. Overrides FormWizardInterface::getParameters 1
FormWizardBase::getPreviousParameters public function The Route parameters for a 'previous' step. Overrides FormWizardInterface::getPreviousParameters
FormWizardBase::getRouteName public function The name of the route to which forward or backwards steps redirect. Overrides FormWizardInterface::getRouteName 2
FormWizardBase::getStep public function Retrieve the current active step of the wizard. Overrides FormWizardInterface::getStep
FormWizardBase::getTempstore public function The active SharedTempStore for this wizard. Overrides FormWizardInterface::getTempstore
FormWizardBase::getTempstoreId public function The shared temp store factory collection name. Overrides FormWizardInterface::getTempstoreId
FormWizardBase::initValues public function Initialize wizard values. Overrides FormWizardInterface::initValues 1
FormWizardBase::populateCachedValues public function Form validation handler that populates the cached values from tempstore. Overrides FormWizardInterface::populateCachedValues
FormWizardBase::previous public function Form submit handler to step backwards in the wizard. Overrides FormWizardInterface::previous
FormWizardBase::submitForm public function Form submission handler. Overrides FormInterface::submitForm
FormWizardBase::validateForm public function Form validation handler. Overrides FormBase::validateForm
FormWizardBase::__construct public function 1
FormWizardInterface::getOperations public function Retrieve a list of FormInterface classes by their step key in the wizard. 2
FormWizardInterface::LOAD_VALUES constant Constant value for wizard load event.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
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. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' 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. 1
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.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.