12.8. Работа с формами в Drupal 8. Добавляем форму администрирования.
В этом уроке мы разберемся с Drupal 8 Form API и создадим форму настроек для модуля. Мы уже создали модули для вывода страницы и блока, давайте теперь создадим конфигурационную форму, в которой мы будем хранить данные для подключению к условному сервису. Допустим, что нам нужно хранить на сайте API Key и API Client ID, например для Google Maps API.
Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8
Мы можем хранить эти данные в settings.php и добавить эти настройки в git. Но это будет не безопасно, хранит доступы к сервисам лучше в базе данных.
Давайте добавим еще один роут для нашей формы:
modules/custom/drupalbook/drupalbook.routing.yml
drupalbook.settings:
path: '/admin/structure/drupalbook/settings'
defaults:
_form: '\Drupal\drupalbook\Form\DrupalbookSettingsForm'
_title: 'DrupalBook Settings form'
requirements:
_permission: 'administer site configuration'
В отличии от прежних роутов в defaults мы указываем не _controller, а _form. Дело в том, что мы будем создавать не Controller класс для формы, а класс формы. Давайте создадим файл для класса формы:
modules/custom/drupalbook/src/Form/DrupalbookSettingsForm.php
Вам нужно будет создать отдельную папку Form в папке src для ваших форм. Это позволяет разделить код модуля по отдельным папкам и вы также легко можете найти нужный вам код ориентируюсь по названием папок.
Добавьте следующий код формы и мы разберем каждый из блоков кода и как это работает:
namespace Drupal\drupalbook\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Configure example settings for this site.
*/
class DrupalbookSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'drupalbook_admin_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'drupalbook.settings',
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('drupalbook.settings');
$form['drupalbook_api_key'] = array(
'#type' => 'textfield',
'#title' => $this->t('API Key'),
'#default_value' => $config->get('drupalbook_api_key'),
);
$form['drupalbook_api_client_id'] = array(
'#type' => 'textfield',
'#title' => $this->t('API Client ID'),
'#default_value' => $config->get('drupalbook_api_client_id'),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Retrieve the configuration
$this->configFactory->getEditable('drupalbook.settings')
// Set the submitted configuration setting
->set('drupalbook_api_key', $form_state->getValue('drupalbook_api_key'))
// You can set multiple configurations at once by making
// multiple calls to set()
->set('drupalbook_api_client_id', $form_state->getValue('drupalbook_api_client_id'))
->save();
parent::submitForm($form, $form_state);
}
}
Мы уже разбирались с namespace и use операторами и что друпал использует их для подключения автоматически только тех классов, которые нужны:
namespace \drupalbook\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
Чтобы создать конфигурационную форму нужно наследоваться от класса ConfigFormBase, ConfigFormBase подразумевает, что вы будете сохранять данные с формы в конфигурацию.
class DrupalbookSettingsForm extends ConfigFormBase {
Дальше указываем Form ID, он должен быть уникальным для каждой формы. Если вы будете писать id вашей формы начиная с названия модуля, то наверняка ваш id будет уникальным:
function getFormId() {
return 'drupalbook_admin_settings';
}
Указываем группу конфигов в которой мы будем хранить данные:
function getEditableConfigNames() {
return [
'drupalbook.settings',
];
}
Теперь давайте разберемся как мы создаем сами поля форм. Вы можете оценить возможности Form API и какие поля вы сможете вывести, прочитав документацию:
https://api.drupal.org/api/drupal/elements/8.5.x
Мы использовали пока что только textfield:
Но вам часто придется сталкиватся с выпадающими списками:
Чекбоксами и радио баттонами:
Попробуйте добавить свои поля в форму, не ограничивайтесь только текстовыми полями.
function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('drupalbook.settings');
Мы формируем массив формы в $form и потом строим из него HTML. Переменная $form_state хранит в себе все данные с формы, которые мы отправляем, здесь и values всех полей, id формы, CSRF-токен для защиты от автоматической отправки формы. $form_state также позволяет передавать данные между шагами мультистеп формы, мы будем это использовать в одном из следующих уроках. Также каждый раз когда форма отправляется через AJAX, форма перестраивается заново, а $form_state позволяет построить копию формы, которая была перед нажатием на кнопку submit. И если форма не будет отправлена из-за ошибки, например какое-то поле не будет заполнено, то все поля будет с сохраненями значениями, которые хранились в $form_state. Поэтому $form и $form_state всегда идут вместе.
Здесь также мы подгружаем данные из конфигов. Возможно вы уже что-то сохранили в drupalbook.settings и $config уже не пустой, это позволит выставить текущие значения в поля с помощью #default_value в каждом из текстовых полей, получая данные из конфигов с помощью метода get().
['drupalbook_api_key'] = array(
'#type' => 'textfield',
'#title' => $this->t('API Key'),
'#default_value' => $config->get('drupalbook_api_key'),
);
$form['drupalbook_api_client_id'] = array(
'#type' => 'textfield',
'#title' => $this->t('API Client ID'),
'#default_value' => $config->get('drupalbook_api_client_id'),
);
И в конце метода мы возвращаем $form и $form_state, чтобы форма была построена.
::buildForm($form, $form_state);
Дальше у нас идет метод submitForm(), он срабатывает если форма была отправлена и никаких ошибок не возникло. Если все-таки у вас было не заполненное обязательное поле и друпал выдает ошибку, то submitForm не будет срабатывать. Если вы хотите проверить значения отправленых данных с формы, то вам нужно использовать validateForm(), validate сработает даже если в форме будет ошибка и с помощью validate можно отменить отправку формы и вызвать ошибку, если что-то вас не устраивает в данных. Мы рассмотрим validate в одном из следующих уроков по формам.
В методе submitForm() мы проходим по всем полям, собираем их значения и обновляем конфигурацию drupalbook.settings:
function submitForm(array &$form, FormStateInterface $form_state) {
// Retrieve the configuration
$this->configFactory->getEditable('drupalbook.settings')
// Set the submitted configuration setting
->set('drupalbook_api_key', $form_state->getValue('drupalbook_api_key'))
// You can set multiple configurations at once by making
// multiple calls to set()
->set('drupalbook_api_client_id', $form_state->getValue('drupalbook_api_client_id'))
->save();
parent::submitForm($form, $form_state);
}
Мы также вызывает родительский метод submitForm в котором выводится сообщение об удачном отправление формы. Вы можете закомментрировать эту строку и написать свое сообщение:
//parent::submitForm($form, $form_state);
drupal_set_message($this->t('My Cool Form have been saved!'));
Не забудьте почистить кеш, чтобы ваш роут применился. Теперь вы можете попробовать вашу форму в действие. Когда вам нужно будет подгрузить API Key, вы можете использовать этот код:
$config = \Drupal::config('example.settings');
$api_key =$config->get('drupalbook_api_key');
$api_client_id = $config->get('drupalbook_api_client_id');
Этот код будет работать в любом модуле или preprocess функции, потому что в друпал единая система конфигурации.
На этом все в следующем уроке по формам, мы разберем как делать мультистеп формы.
Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8