Simple Configuration in Drupal: Using config.get() and config.set()for Drupal 11 , and 10

Last updated :  

Every real Drupal module eventually needs to remember something between page requests — a message to display, an API key, a toggle an administrator flipped on a settings page. Where do you put that? Not in a database table you design yourself, and definitely not in a PHP constant baked into your code. Drupal has a purpose-built system for exactly this: the Configuration API, and this lesson teaches you its two most important methods: get() and set().

We'll study a real, working example from Drupal's own Examples project — the config_simple_example module — which does the smallest possible useful thing: it lets a site administrator type a message into a textarea, save it, and have that message remembered forever (or until someone changes it again).

Configuration vs. content — why this isn't just "another database table"

If you're coming from general web development, your first instinct might be to reach for a database table and some raw SQL. Drupal deliberately steers you away from that for settings like this, and the reason matters: configuration in Drupal means "small, structured, site-specific settings" — things like a module's on/off toggle, an API endpoint URL, or a welcome message. Unlike content (nodes, users, comments — the stuff visitors create), configuration is meant to be exported to YAML files, committed to version control, and deployed identically across your dev, staging, and production environments.

That's the payoff for learning this API instead of rolling your own: anything you store through it is automatically exportable, importable, and — as you'll see in a later lesson — translatable, with zero extra database schema work on your part.

What you'll learn in this lesson

  • What Drupal's Configuration API is for, and how it differs from storing content in the database
  • How to read a stored configuration value with $config->get()
  • How to write a new configuration value with ->set()->save()
  • Why ConfigFormBase is the class you extend the moment a form needs to persist settings
A quick heads-up: this same source file — ConfigSimpleExampleSettingsForm.php — comes up again in the very next lesson, where we look at it from a different angle: the ConfigFormBase contract itself, rather than the get/set calls. Seeing the same real code twice, through two different lenses, is intentional — it's how the concepts actually click into place.

The source file

Path (relative to the Examples module's root): modules/config_simple_example/src/Form/ConfigSimpleExampleSettingsForm.php

<?php

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);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Retrieve the configuration.
    $this->config('config_simple_example.settings')
      // Set the submitted configuration setting.
      ->set('message', $form_state->getValue('message'))
      ->save();

    parent::submitForm($form, $form_state);
  }

}

How it works

Extending ConfigFormBase instead of FormBase

class ConfigSimpleExampleSettingsForm extends ConfigFormBase {

If you've written a plain Drupal form before, you're used to extending FormBase. Here we extend its more specialized sibling, ConfigFormBase, which adds exactly two things a settings form needs: a protected config() helper method, and automatic access to the config factory service — no manual dependency injection required. Every Drupal form must satisfy the FormInterface contract, and ConfigFormBase already handles that for you except for the three methods you see here.

getEditableConfigNames() — the permission list

protected function getEditableConfigNames() {
  return [
    'config_simple_example.settings',
  ];
}

Think of this as a guest list. It's an array of configuration object names this form is allowed to write to. When you later call $this->config() inside submitForm(), Drupal checks this list — only names on it come back as a fully writable Config object. Ask for anything else and you get a read-only ImmutableConfig instead, which quietly protects you from accidentally modifying configuration your form was never meant to touch.

The naming pattern module_name.object_name (here, config_simple_example.settings) is a Drupal-wide convention. The module name prefix keeps two different modules from ever colliding on the same config key.

Reading a value: config.get()

$config = $this->config('config_simple_example.settings');

$form['message'] = [
  '#type' => 'textarea',
  '#title' => $this->t('Message'),
  '#default_value' => $config->get('message'),
];

This is the "get" half of the lesson. $config->get('message') reads the value stored under the message key inside the config_simple_example.settings configuration object — the actual data lives as YAML in Drupal's active configuration store (the database, by default). Whatever comes back becomes the textarea's #default_value, so an administrator opening this form sees the current setting instead of a blank box. If nothing has ever been saved, this call falls back to the default shipped in config/install/config_simple_example.settings.yml.

Writing a value: config.set()

$this->config('config_simple_example.settings')
  ->set('message', $form_state->getValue('message'))
  ->save();

And here's the "set" half, which runs when the form is submitted. Read it as three chained steps:

  1. $this->config('config_simple_example.settings') returns a mutable Config object this time — because submitForm() is where the "guest list" from getEditableConfigNames() actually grants write access.
  2. ->set('message', $form_state->getValue('message')) updates the in-memory object with whatever the administrator just typed. Nothing is saved to the database yet — this only affects the object sitting in PHP memory. set() returns $this, which is what lets you chain the next call directly onto it.
  3. ->save() is the step that actually persists the change, writing it to the active configuration store so it survives the current request and every one after it.
Common beginner mistake: forgetting to call ->save(). Calling ->set() alone changes an in-memory PHP object and nothing else — if the page reloads without save() having run, your change is gone as if it never happened. The two always travel together.

Why the values still need a default and a schema

Two supporting YAML files make this form fully correct, even though neither one is PHP code:

  • config/install/config_simple_example.settings.yml ships the value message: 'Awesome settings' as the default installed the moment the module is enabled — without it, $config->get('message') would return NULL on a fresh site.
  • config/schema/config_simple_example.schema.yml declares that message is of type text, which is what makes it eligible for translation later. You'll meet schema files properly in the last lesson of this topic.

See it for yourself

On your own DDEV site, visit /admin/config/form-api-example/config-simple-form, type a new message into the textarea, and click Save configuration.

The Config Simple Example settings form on Drupal admin showing a saved configuration message

Notice the confirmation message and — most importantly — reload the page. The value you typed is still there. That's config.get() reading back exactly what config.set() wrote, proving the round trip actually works.

Quick check: if you deleted the config/install/config_simple_example.settings.yml file entirely and reinstalled the module fresh, what would $config->get('message') return the very first time the form loads? If you said NULL, you've understood the role of that install file.

Key takeaways

  • Extend ConfigFormBase (not FormBase) whenever your form reads or writes Drupal configuration — it provides the config() helper and wires up submit messaging automatically.
  • Declare every configuration object your form writes to in getEditableConfigNames() — that's what grants write permission and returns a mutable Config object inside submitForm().
  • Use $config->get('key') in buildForm() to read the current stored value and pre-populate form fields.
  • Use $this->config('name')->set('key', $value)->save() in submitForm() to persist a new value — set() alone changes nothing permanently without save().
  • Always ship a config/install/module.settings.yml with sensible defaults, so config.get() never returns NULL immediately after installation.

Coming up next

You've now seen the get()/set() pattern that sits at the heart of every Drupal settings form. In the next lesson we'll return to this exact same file, but zoom out to look at the bigger picture: the full contract ConfigFormBase expects every config form to fulfill, method by method — so you know precisely what's required versus what's just convention.