Обычно вы можете хранить значения форм между шагами, используя кеш объектов cTools (аналогично многоступенчатым формам в Drupal 7 ) или через $form_state
(согласно этому уроку ).
В Drupal 8 вы можете наследовать FormBase
класс для создания нового многошагового класса.
В статье Как создавать многошаговые формы в Drupal 8 вы можете найти простой способ создания многошаговой формы в Drupal 8.
Прежде всего, вам нужно создать базовый класс, который будет отвечать за внедрение необходимых зависимостей.
Мы сгруппируем все классы форм вместе и поместим их в новую папку с именем, Multistep
расположенную в Form
каталоге плагинов нашего демонстрационного модуля. Это просто для того, чтобы иметь чистую структуру и возможность быстро определить, какие формы являются частью нашего многоэтапного процесса форм.
Вот демонстрационный код (для MultistepFormBase.php
файла):
/**
* @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);
}
}
}
Затем вы можете создать реальный класс форм внутри файла с именем 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');
}
}
В этом buildForm()
методе мы определяем два элемента фиктивной формы. Обратите внимание, что мы сначала получаем существующее определение формы из родительского класса. Значения по умолчанию для этих полей устанавливаются как значения, найденные в хранилище для этих ключей (чтобы пользователи могли видеть значения, которые они заполнили на этом шаге, если вернутся к нему). Наконец, мы меняем значение кнопки действия на Далее (чтобы указать, что эта форма не является окончательной).
В submitForm()
методе мы сохраняем отправленные значения в хранилище, а затем перенаправляем во вторую форму (которую можно найти на маршруте demo.multistep_two
). Имейте в виду, что мы не проводим здесь никакой проверки, чтобы сохранить свет в коде. Но большинство сценариев использования требуют проверки входных данных.
И обновите файл маршрутизации в демонстрационном модуле ( 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'
Наконец, создайте вторую форму ( 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');
}
}
Внутри submitForm()
метода мы снова сохраняем значения в хранилище и передаем родительский класс, чтобы сохранить эти данные любым удобным для них способом. Затем мы перенаправляем на любую страницу, которую хотим (маршрут, который мы здесь используем, является фиктивным).
Теперь у нас должна быть рабочая многошаговая форма, которая использует PrivateTempStore
данные для доступа к нескольким запросам. Если нам нужно больше шагов, все, что нам нужно сделать, это создать еще несколько форм, добавить их между существующими и внести пару корректировок.