9.8. Drupal でのフォームの操作。プログラムで構成フォームを追加する。
このチュートリアルでは、Drupal Form API を扱い、モジュール用の設定フォームを作成します。すでにページとブロックを表示するモジュールを作成しました。次に、仮のサービスへの接続用のデータを保存する構成フォームを作成しましょう。例えば、Google Maps API のために、サイトに API Key と API Client ID を保存する必要があるとします。
コード例は github で見られます:
https://github.com/levmyshkin/drupalbook8
このデータを settings.php に保存し、これらの設定を git に追加することもできます。しかし、それは安全ではありません。サービスへのアクセス情報はデータベースに保存するほうが良いです。

フォーム用にもう 1 つルートを追加しましょう:
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
フォーム用に、src フォルダー内に別の Form フォルダーを作成する必要があります。これにより、モジュールのコードを別々のフォルダーに分けられ、フォルダーの名前に基づいて必要なコードを簡単に見つけられます。
次のフォームコードを追加します。各コードブロックとそれがどう動作するかを分析します:
<?php
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 演算子、そして Drupal がそれらを使って必要なクラスだけを自動的に接続することは、すでに扱いました:
\drupalbook\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
構成フォームを作成するには、ConfigFormBase クラスから継承する必要があります。ConfigFormBase は、フォームのデータを構成に保存することを意味します。
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 は、私たちが送信するフォームからのすべてのデータをそれ自身に保存します。ここには、すべてのフィールドの値、フォーム id、自動フォーム送信から保護するための CSRF トークンがあります。$form_state はまた、マルチステップフォームのステップ間でデータを移すこともでき、これは次のレッスンの 1 つで使います。また、フォームが AJAX 経由で送信されるたびに、フォームは再び再構築され、$form_state を使うと submit ボタンをクリックする前にあったフォームのコピーを構築できます。そして、フォームがエラー、例えば一部のフィールドが埋められていないために送信されない場合、すべてのフィールドは $form_state に保存されていた値で保存されます。したがって、$form と $form_state は常に一緒に使われます。
ここでは構成からもデータを読み込みます。おそらく、すでに drupalbook.settings に何かを保存していて $config はもはや空ではないでしょう。これにより、get() メソッドを使って構成からデータを受け取り、各テキストフィールドで #default_value を使ってフィールドに現在の値を設定できます。
['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() メソッドがあります。これは、フォームが送信されエラーが発生しなかった場合に発火します。それでも、埋められていない必須フィールドがあり Drupal がエラーを投げた場合、submitForm は機能しません。フォームから送信されたデータの値を確認したい場合は、validateForm() を使う必要があります。validate はフォームにエラーがあっても機能し、validate を使ってデータに気に入らない点があればフォームの送信をキャンセルしてエラーを発生させられます。validate は次のフォームチュートリアルの 1 つで扱います。
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 関数で機能します。というのも、Drupal には単一の構成システムがあるからです。
フォームに関する次のレッスンではこれで終わりです。マルチステップフォームの作り方を分析します。
コード例は github で見られます:
https://github.com/levmyshkin/drupalbook8