Normalement, vous pouvez stocker des valeurs de formulaire entre les étapes à l'aide du cache d'objets cTools (similaire aux formulaires à plusieurs étapes dans Drupal 7 ), ou via le $form_state
(comme dans ce tutoriel ).
Dans Drupal 8, vous pouvez hériter de la FormBase
classe pour créer une nouvelle classe à plusieurs étapes.
Dans l'article Comment créer des formulaires en plusieurs étapes dans Drupal 8, vous pouvez trouver un moyen simple de créer un formulaire en plusieurs étapes dans Drupal 8.
Tout d'abord, vous devrez créer la classe de base qui sera chargée d'injecter les dépendances nécessaires.
Nous regrouperons toutes les classes de formulaires et les placerons dans un nouveau dossier appelé Multistep
situé dans le Form
répertoire des plugins de notre module de démonstration. Ceci est purement pour avoir une structure propre et pouvoir dire rapidement quelles formes font partie de notre processus de formulaire en plusieurs étapes.
Voici le code de démonstration (pour le MultistepFormBase.php
fichier):
/**
* @file
* Contains \Drupal\demo\Form\Multistep\MultistepFormBase.
*/
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\SessionManagerInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class MultistepFormBase extends FormBase {
/**
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* @var \Drupal\Core\Session\SessionManagerInterface
*/
private $sessionManager;
/**
* @var \Drupal\Core\Session\AccountInterface
*/
private $currentUser;
/**
* @var \Drupal\user\PrivateTempStore
*/
protected $store;
/**
* Constructs a \Drupal\demo\Form\Multistep\MultistepFormBase.
*
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* @param \Drupal\Core\Session\SessionManagerInterface $session_manager
* @param \Drupal\Core\Session\AccountInterface $current_user
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, SessionManagerInterface $session_manager, AccountInterface $current_user) {
$this->tempStoreFactory = $temp_store_factory;
$this->sessionManager = $session_manager;
$this->currentUser = $current_user;
$this->store = $this->tempStoreFactory->get('multistep_data');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.private_tempstore'),
$container->get('session_manager'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Start a manual session for anonymous users.
if ($this->currentUser->isAnonymous() && !isset($_SESSION['multistep_form_holds_session'])) {
$_SESSION['multistep_form_holds_session'] = true;
$this->sessionManager->start();
}
$form = array();
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Submit'),
'#button_type' => 'primary',
'#weight' => 10,
);
return $form;
}
/**
* Saves the data from the multistep form.
*/
protected function saveData() {
// Logic for saving data goes here...
$this->deleteStore();
drupal_set_message($this->t('The form has been saved.'));
}
/**
* Helper method that removes all the keys from the store collection used for
* the multistep form.
*/
protected function deleteStore() {
$keys = ['name', 'email', 'age', 'location'];
foreach ($keys as $key) {
$this->store->delete($key);
}
}
}
Ensuite, vous pouvez créer la classe de formulaires réelle dans un fichier appelé MultistepOneForm.php
:
/**
* @file
* Contains \Drupal\demo\Form\Multistep\MultistepOneForm.
*/
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormStateInterface;
class MultistepOneForm extends MultistepFormBase {
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'multistep_form_one';
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$form['name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your name'),
'#default_value' => $this->store->get('name') ? $this->store->get('name') : '',
);
$form['email'] = array(
'#type' => 'email',
'#title' => $this->t('Your email address'),
'#default_value' => $this->store->get('email') ? $this->store->get('email') : '',
);
$form['actions']['submit']['#value'] = $this->t('Next');
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('email', $form_state->getValue('email'));
$this->store->set('name', $form_state->getValue('name'));
$form_state->setRedirect('demo.multistep_two');
}
}
Dans la buildForm()
méthode, nous définissons nos deux éléments de formulaire fictifs. Notez que nous récupérons d'abord la définition de formulaire existante dans la classe parent. Les valeurs par défaut de ces champs sont définies comme les valeurs trouvées dans le magasin pour ces clés (afin que les utilisateurs puissent voir les valeurs qu'ils ont remplies à cette étape s'ils y reviennent). Enfin, nous modifions la valeur du bouton d'action sur Suivant (pour indiquer que ce formulaire n'est pas le dernier).
Dans la submitForm()
méthode, nous enregistrons les valeurs soumises dans le magasin, puis redirige vers le deuxième formulaire (qui peut être trouvé sur l'itinéraire demo.multistep_two
). Gardez à l'esprit que nous ne faisons aucune sorte de validation ici pour garder le code léger. Mais la plupart des cas d'utilisation exigeront une certaine validation d'entrée.
Et mettez à jour votre fichier de routage dans le module de démonstration ( demo.routing.yml
):
demo.multistep_one:
path: '/demo/multistep-one'
defaults:
_form: '\Drupal\demo\Form\Multistep\MultistepOneForm'
_title: 'First form'
requirements:
_permission: 'access content'
demo.multistep_two:
path: '/demo/multistep-two'
defaults:
_form: '\Drupal\demo\Form\Multistep\MultistepTwoForm'
_title: 'Second form'
requirements:
_permission: 'access content'
Enfin, créez le second formulaire ( MultistepTwoForm
):
/**
* @file
* Contains \Drupal\demo\Form\Multistep\MultistepTwoForm.
*/
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
class MultistepTwoForm extends MultistepFormBase {
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'multistep_form_two';
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$form['age'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your age'),
'#default_value' => $this->store->get('age') ? $this->store->get('age') : '',
);
$form['location'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your location'),
'#default_value' => $this->store->get('location') ? $this->store->get('location') : '',
);
$form['actions']['previous'] = array(
'#type' => 'link',
'#title' => $this->t('Previous'),
'#attributes' => array(
'class' => array('button'),
),
'#weight' => 0,
'#url' => Url::fromRoute('demo.multistep_one'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('age', $form_state->getValue('age'));
$this->store->set('location', $form_state->getValue('location'));
// Save the data
parent::saveData();
$form_state->setRedirect('some_route');
}
}
À l'intérieur de la submitForm()
méthode, nous enregistrons à nouveau les valeurs dans le magasin et nous reportons à la classe parente pour conserver ces données de la manière qu'elle juge appropriée. Nous redirigeons ensuite vers la page que nous voulons (l'itinéraire que nous utilisons ici est factice).
Nous devrions maintenant avoir un formulaire en plusieurs étapes qui utilise le PrivateTempStore
pour garder les données disponibles sur plusieurs demandes. Si nous avons besoin de plus d'étapes, tout ce que nous avons à faire est de créer d'autres formulaires, de les ajouter entre les formulaires existants et de faire quelques ajustements.