Using config.factory to save module settingsfor Drupal 8 , 9 , 10 , and 11

Last updated :  

Last lesson gave the weather module's settings a schema — a label describing what shape the data should be. This lesson is about actually writing data into that shape. In Drupal, you never touch the config database table directly; you always go through the config.factory service, which is Drupal's single front door for reading and writing configuration, no matter which module or which part of core is asking.

What you'll learn in this lesson

  • How to get an editable config object from config.factory
  • Why getEditable() and get() are different calls with different intents
  • The set() / save() pattern for writing configuration
  • Where this actually gets called from in a real module (a settings form's submitForm())

Saving the weather module's settings

Picture the settings form for our weather module: an API key field and a default location field. When a site builder submits it, the form's submitForm() needs to persist both values:

public function submitForm(array &$form, FormStateInterface $form_state) {
  $this->config('weather_module.settings')
    ->set('api_key', $form_state->getValue('api_key'))
    ->set('default_location', $form_state->getValue('default_location'))
    ->save();

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

If this class extends Drupal's ConfigFormBase, the $this->config() helper is already available and returns an editable config object for the name you pass it — this is really just a convenience wrapper around \Drupal::service('config.factory')->getEditable('weather_module.settings'). set() is chainable, so you can stack up every field's value before a single save() call writes the whole object to the database in one go.

getEditable() vs. the read-only get()

config.factory actually gives you two different ways to load the same config object, and the distinction matters: get('weather_module.settings') returns an immutable object — call set() on it and Drupal throws an exception, on purpose. getEditable('weather_module.settings') returns a mutable one you're allowed to write to. This isn't bureaucracy for its own sake: most of your module's code only ever needs to read config (rendering a block, checking a setting), and using the immutable form there means Drupal can safely cache and share that object without worrying that some unrelated code accidentally mutated it.

Real Examples module reference: cache_example's settings form (in the Drupal Examples project) uses exactly this $this->config('cache_example.settings')->set(...)->save() pattern to persist the message shown on its demo page — worth reading alongside this lesson.

Quick check: some other code in your codebase calls \Drupal::config('weather_module.settings') (not getEditable) and then tries to call ->set() on it. What happens? (Answer: a ConfigDataModifiedException or similar hard failure — \Drupal::config() always returns the immutable form, exactly like config.factory's plain get(). Only getEditable() lets you write.)

Key takeaways

  • All configuration reads and writes go through the config.factory service — never query the config table directly.
  • getEditable($name) returns a mutable object you can set() and save(); the plain get($name) (and \Drupal::config()) return an immutable, read-only one.
  • set() is chainable — stack every field before calling save() once.
  • A ConfigFormBase subclass's $this->config($name) helper already returns the editable form, ready to use inside submitForm().

Coming up next

Saving is only half the story — the next lesson covers reading those same values back out programmatically, so the rest of your module (not just the settings form) can actually use the API key and default location a site builder configured.