Think of your module's configuration form as the dashboard of a car. The engine (your module's actual logic) can run on defaults forever, but a dashboard lets the driver — a site administrator, not a developer — adjust things without ever popping the hood. Drupal gives you a purpose-built base class for exactly this: ConfigFormBase. It looks like an ordinary form, but it comes wired up to Drupal's Configuration API for free, so every value the administrator types in gets saved as real, exportable site configuration rather than a one-off database row you'd have to manage yourself.
What you'll learn in this lesson
- Why
ConfigFormBaseexists and what it gives you over a plainFormBaseform - The two methods every settings form must implement:
getFormId()andgetEditableConfigNames() - How to read a stored config value into a form field's
#default_value - How to route an admin-only settings page so it actually appears somewhere a site builder can find it
Extending ConfigFormBase
namespace Drupal\config_simple_example\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Configure example settings for this site.
*/
class ConfigSimpleExampleSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_simple_example_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'config_simple_example.settings',
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('config_simple_example.settings');
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
'#default_value' => $config->get('message'),
];
return parent::buildForm($form, $form_state);
}
}
This is real, working code from Drupal's own Examples project (config_simple_example). Two methods make this a config form rather than an ordinary one:
getEditableConfigNames() — declaring what this form is allowed to touch
Every config form must return the machine names of the config objects it edits. Drupal uses this list for two things: it locks the named config to this specific form during the request (so a rogue alter hook can't sneak in an edit to something the form never asked for), and it's how core's configuration-override system knows which forms are affected by an active override. One form can list several config object names if it manages settings that live in more than one place.
Reading the current value with $this->config()
$config = $this->config('config_simple_example.settings');
$form['message']['#default_value'] = $config->get('message');
$this->config() — available because you extended ConfigFormBase — loads the named config object read-only. Calling ->get('message') pulls out one key. Wiring that straight into #default_value is what makes the form show the site's actual current setting rather than always starting blank.
Routing the form so someone can reach it
config_simple_example.settings:
path: '/examples/config-simple-example/settings'
defaults:
_form: '\Drupal\config_simple_example\Form\ConfigSimpleExampleSettingsForm'
_title: 'Configuration Simple Example Settings Form'
requirements:
_permission: 'administer site configuration'
Notice the default key here is _form, not _controller — that's the routing shortcut reserved for form classes; Drupal instantiates the form and calls its buildForm() automatically. _permission: 'administer site configuration' is doing real work: without it, any authenticated visitor could reach your settings page directly by URL.
_permission (or a too-generic one like 'access content') is reachable by anyone who guesses or finds the URL — configuration forms should almost always require 'administer site configuration' or a permission specific to your module.Quick check: if you added a second setting — say, a checkbox called
show_banner— stored in the sameconfig_simple_example.settingsobject, would you need a second entry ingetEditableConfigNames()? (Answer: no — that method lists config objects, not individual keys within them. One object can hold as many settings as you want; you'd only add a second entry if the new setting lived in a completely different config object.)
Key takeaways
ConfigFormBasegives you Configuration API integration for free — extend it instead ofFormBasewhenever a form's whole job is editing site settings.getEditableConfigNames()must list every config object this form edits — it's a security and override-tracking mechanism, not decoration.getFormId()still works exactly like any other Form API class — it's the form's unique machine-readable ID.- Read existing values with
$this->config('your.config.name')->get('key')and wire them into#default_valueso the form reflects reality on load. - Always route settings forms behind a real permission —
'administer site configuration'at minimum.
Coming up next
Building the form is only half the job — right now, submitting it does nothing. In the next lesson, you'll implement submitForm() to actually persist what the administrator typed into permanent site configuration.