Setting plugins¶
Add per-site settings with a #[SiteSetting] plugin, map them to
configuration, and read their values in code.
What a setting plugin is¶
A setting plugin declares a group of setting keys. It gets a section on
each site whose type offers one of its keys, at
/admin/sites/{site}/settings/{plugin_id} (permission Manage site
settings), a row per key in every site type's matrix, and an entry in the
settings reference. How the layers of a value
combine is on Setting layers.
Keys share one namespace across all plugins; a plugin that computes its keys
prefixes them, as Roles does with per_site_role:{rid}.
plugin.manager.site_setting discovers the plugins in Plugin/SiteSetting;
hook_site_setting_info_alter() alters them.
Declare a plugin¶
namespace Drupal\my_module\Plugin\SiteSetting;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\sites\Attribute\SiteSetting;
use Drupal\sites\Plugin\SiteSetting\SiteSettingPluginBase;
use Drupal\sites\SiteSetting\SiteSettingValuesInterface;
#[SiteSetting(
id: 'my_module_support',
label: new TranslatableMarkup('Support'),
description: new TranslatableMarkup('Where visitors of the site get help.'),
weight: 60,
settings: [
'support_phone' => [
'label' => new TranslatableMarkup('Support phone'),
'default' => '',
'translatable' => TRUE,
],
'maintenance_message' => [
'label' => new TranslatableMarkup('Maintenance message'),
'default' => '',
'translatable' => TRUE,
'config_target' => 'system.maintenance:message',
],
],
)]
final class Support extends SiteSettingPluginBase implements SiteSettingValuesInterface {
public function buildSettingsForm(array $form, FormStateInterface $form_state): array {
$form['support_phone'] = [
'#type' => 'tel',
'#title' => $this->t('Support phone'),
'#default_value' => $this->settings['support_phone'],
];
$form['maintenance_message'] = [
'#type' => 'textarea',
'#title' => $this->t('Maintenance message'),
'#default_value' => $this->settings['maintenance_message'],
];
return $form;
}
public function validateValues(array $values): array {
return $this->typeErrors($values);
}
}
The manager reads the key definitions in settings without instantiating
the plugin. Keys that depend on runtime state (bundles, roles, menus) come
from defineSettings() instead; sites call it while they resolve their
settings, so it must not load sites. section_of puts the form on another
plugin's section, after the key section_after names, as Sites path prefix
does on the identity section.
Definition keys¶
| Key | Default | Effect |
|---|---|---|
label |
Names the key in the site type's matrix and in token lists. | |
default |
The value when neither the site nor its type sets one. typeErrors() checks values against its type. |
|
translatable |
FALSE |
The value may differ per language. |
environment |
TRUE |
The value may differ per environment. |
locked |
FALSE |
Every site type offers the key, and each site stores its own value. |
config |
TRUE |
FALSE makes a structural key: outside the matrix, edited on the site's own layer only. A plugin of structural keys only has no section. |
config_target |
The configuration value the key is stored as, a per-site override. See below. | |
sensitive |
FALSE |
Redacted in output and tokens; sites.site_settings refuses to write it. |
inheritable |
TRUE |
Whether a site may take the value from the site it inherits from; the site type's matrix decides whether it does. FALSE by itself for a locked key without a config target (the site's identity) and for a sensitive key. |
file |
FALSE |
The value is a file URI; the site is registered as a user of the file. |
The site type decides per key whether the environment and language layers exist; these flags decide whether it may.
The section form¶
buildSettingsForm() builds one element per key, named after the key,
optionally inside details, fieldset or container groups, with values
from $this->settings. The same form edits the type's defaults and every
layer of a site: the section drops the elements of keys the layer does not
offer and stores only values that differ from the layer below.
settingsValueCallback() converts a submitted value into its stored shape.
SiteSettingValuesInterface::validateValues() receives values in their
stored shape and returns error messages keyed by setting key. The base
validateSettingsForm() shows them on the form; sites.site_settings
applies them to every write from an agent tool or your code, and refuses
plugins without the interface.
Config targets¶
config_target takes config.name:property.path or, in defineSettings(),
a Drupal\Core\Form\ConfigTarget with fromConfig and toConfig
callables. A site entity stores the value as a per-site configuration
override row per language and environment, not as a setting; a code site
derives it in configOverrides(). Configuration on the site's host returns
the value.
Every site carries a key's default. SiteSettingPluginBase::configOverrides()
leaves out an empty value (NULL, [], blank text) of a key whose target is
a configuration object every site shares (config.name:path or a
ConfigTarget), so the platform's value stands. A placeholder target
(…sites__site_id…) names the site's own object and takes every value as it
is. Override configOverrides() for other values that mean "keep the
platform's", as SystemSite does for the front page /. SitesAnalytics
overrides it to compute overrides from several keys.
Configuration read while the overrides are built comes without site
overrides. See Config overrides.
Read settings¶
$phone = $this->currentSite->getSetting('support_phone');
$hydrion_phone = $this->siteManager->getSite('hydrion')?->getSetting('support_phone');
getSetting() returns the resolved value for the current language and
environment, with inheritance and settings.php applied.
sites.site_settings->read($plugin_id, SettingLayer::base($site_id))
returns one layer's values and the keys it stores itself (own);
writeErrors() and write() change it. Show resolved settings only through
plugin.manager.site_setting->redact($site, $settings).
settings.php¶
A server pins a value in settings.php, keyed by site ID, then setting key:
$settings['hydrion'] = [
'support_phone' => '+49 30 1234567',
'maintenance_message' => 'Hydrion is down for maintenance.',
];
- In
getSetting(), the value wins over every layer:SiteSettingsEvent::BUILDapplies it at priority 0, after main-site inheritance at priority 120. - For a key with a config target, it wins in configuration over the site's stored override.
- Core's
$configinsettings.phpwins over every site's overrides.
Keep hostnames out of settings.php; see
Setting layers.
Last updated 2026-10-15