Normalmente è possibile memorizzare i valori dei moduli tra i passaggi utilizzando la cache degli oggetti cTools (simile ai moduli Multistep in Drupal 7 ) o tramite $form_state
(come da questa esercitazione ).
In Drupal 8 puoi ereditare la FormBase
classe per creare una nuova classe a più fasi.
Nell'articolo Come creare moduli a più passaggi in Drupal 8 puoi trovare un modo semplice per creare un modulo a più fasi in Drupal 8.
Prima di tutto, dovresti creare la classe base che sarà incaricata di iniettare le dipendenze necessarie.
Raggrupperemo tutte le classi del modulo e le inseriremo in una nuova cartella chiamata Multistep
situata nella Form
directory dei plugin del nostro modulo demo. Questo è puramente per avere una struttura pulita ed essere in grado di dire rapidamente quali forme fanno parte del nostro processo di moduli a più fasi.
Ecco il codice demo (per MultistepFormBase.php
file):
/**
* @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);
}
}
}
Quindi è possibile creare la classe di moduli effettiva all'interno di un file chiamato 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');
}
}
Nel buildForm()
metodo stiamo definendo i nostri due elementi fittizi. Si noti che stiamo recuperando prima la definizione del modulo esistente dalla classe genitore. I valori predefiniti per questi campi sono impostati come valori trovati nell'archivio per quelle chiavi (in modo che gli utenti possano vedere i valori che hanno compilato in questo passaggio se tornano ad esso). Infine, stiamo modificando il valore del pulsante di azione su Avanti (per indicare che questo modulo non è quello finale).
Nel submitForm()
metodo salviamo i valori inviati nel negozio e quindi reindirizziamo al secondo modulo (che può essere trovato sul percorso demo.multistep_two
). Tieni presente che non stiamo eseguendo alcun tipo di convalida qui per mantenere il codice leggero. Ma la maggior parte dei casi d'uso richiederà una convalida dell'input.
E aggiorna il tuo file di routing nel modulo demo ( 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'
Infine, crea il secondo form ( 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');
}
}
All'interno del submitForm()
metodo salviamo nuovamente i valori nell'archivio e li rimandiamo alla classe genitore per conservare questi dati nel modo che ritengono opportuno. Quindi reindirizziamo a qualsiasi pagina desideriamo (il percorso che utilizziamo qui è fittizio).
Ora dovremmo avere un modulo multistep funzionante che utilizza il PrivateTempStore
per mantenere i dati disponibili su più richieste. Se abbiamo bisogno di più passaggi, tutto ciò che dobbiamo fare è creare altri moduli, aggiungerli tra quelli esistenti e apportare un paio di modifiche.