Zwykle wartości formularzy można przechowywać między krokami za pomocą pamięci podręcznej obiektów cTools (podobnie jak formularze Multistep w Drupal 7 ) lub poprzez $form_state(zgodnie z tym przewodnikiem ).
W Drupal 8 możesz odziedziczyć FormBaseklasę, aby utworzyć nową klasę wieloetapową.
W artykule Jak budować wieloetapowe formularze w Drupal 8 , możesz znaleźć prosty sposób na utworzenie wieloetapowego formularza w Drupal 8.
Przede wszystkim musisz stworzyć klasę podstawową, która będzie odpowiedzialna za wprowadzanie niezbędnych zależności.
Zgrupujemy wszystkie klasy formularzy razem i umieścimy je w nowym folderze o nazwie Multistepznajdującym się w Formkatalogu wtyczek naszego modułu demonstracyjnego. Jest to spowodowane czystą strukturą i możliwością szybkiego stwierdzenia, które formularze są częścią naszego wieloetapowego procesu tworzenia formularzy.
Oto kod demonstracyjny (dla MultistepFormBase.phppliku):
/**
* @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);
}
}
}
Następnie możesz utworzyć rzeczywistą klasę formularzy w pliku o nazwie 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');
}
}
W buildForm()metodzie definiujemy nasze dwa atrapy elementów formularza. Zauważ, że najpierw pobieramy istniejącą definicję formularza z klasy nadrzędnej. Wartości domyślne dla tych pól są ustawiane jako wartości znalezione w sklepie dla tych kluczy (aby użytkownicy mogli zobaczyć wartości, które wypełnili na tym etapie, jeśli wrócą do niego). Na koniec zmieniamy wartość przycisku akcji na Dalej (aby wskazać, że ten formularz nie jest ostateczny).
W submitForm()metodzie zapisujemy przesłane wartości do sklepu, a następnie przekierowujemy do drugiego formularza (który można znaleźć na trasie demo.multistep_two). Pamiętaj, że nie przeprowadzamy tutaj żadnego sprawdzania poprawności kodu. Ale większość przypadków użycia wymaga pewnej weryfikacji danych wejściowych.
I zaktualizuj plik routingu w module demonstracyjnym ( 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'
Na koniec utwórz drugą 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');
}
}
Wewnątrz submitForm()metody ponownie zapisujemy wartości w sklepie i przechodzimy do klasy nadrzędnej, aby zachować te dane w dowolny sposób, który uzna za odpowiedni. Następnie przekierowujemy na dowolną stronę, którą chcemy (trasa, którą tu używamy, jest obojętna).
Powinniśmy teraz mieć działający formularz wieloetapowy, który używa PrivateTempStoredo przechowywania danych w wielu żądaniach. Jeśli potrzebujemy więcej kroków, musimy tylko stworzyć więcej formularzy, dodać je między już istniejącymi i wprowadzić kilka zmian.