Drupal - What is the appropriate way to store arrays of information with configuration API?
You define a schema type sequence
:
Example: /core/modules/options/config/schema/options.schema.yml
# Schema for the configuration files of the Options module.
field.storage_settings.list_integer:
type: mapping
label: 'List (integer) settings'
mapping:
allowed_values:
type: sequence
label: 'Allowed values list'
sequence:
type: mapping
label: 'Allowed value with label'
mapping:
value:
type: integer
label: 'Value'
label:
type: label
label: 'Label'
You'll find the form for this example in /core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
More info https://www.drupal.org/docs/8/api/configuration-api/configuration-schemametadata
I was recently struggling how to store data with a nested array of strings,
allowed_view_modes:
image:
embed: embed
full: full
The only way I could figure out how to validate it was by running functional tests, where it validates the configs when you save a config entity.
I was able to save data like the above data by nesting 'sequences':
ckeditor.plugin.drupalmedia:
type: mapping
label: 'Media Embed'
mapping:
allowed_view_modes:
type: sequence
label: 'Allowed View Modes'
nullable: true
sequence:
type: sequence
nullable: true
label: 'View Mode'
sequence:
type: string
This worked and passed the config linter.
The top level sequence is for the media type, the inner sequence is for each view mode. There are lots of complex examples in drupal core. Unfortunately, I don't know where to point you for the documentation. I've mostly been learning this by trial and error.
This was custom openid_connect plugin I was working on, so a bit different than a ConfigFormBase normal configuration form, but I hope this methodology applies and is helpful to someone in the future. I used a schema like so (snippet):
# Schema for the configuration files of the OpenID Connect module.
openid_connect.settings.nimble:
type: config_object
label: 'OpenID Connect Nimble settings'
mapping:
enabled:
type: boolean
label: 'Enable client'
settings:
type: mapping
mapping:
roles_membership_types:
type: sequence
label: 'Role Membership Types'
sequence:
type: mapping
label: 'Roles mapped to membership types'
mapping:
role:
type: string
label: 'Role id'
membership_type:
type: string
label: 'Membership Type'
FAPI setup like so (I had NO form index for the actual name of the config item, roles_membership_types:
$roles = user_role_names(TRUE);
if (isset($roles['authenticated'])) {
unset($roles['authenticated']);
}
$membership_types = ['' => 'None'] + $this->getMembershipTypes($this->configuration['instance']);
$roles_membership_types = $this->configuration['roles_membership_types'];
if (!empty($roles_membership_types)) {
foreach ($roles_membership_types as $type) {
$roles_membership_types_default_values[$type['role']] = $type['membership_type'];
}
}
foreach ($roles as $rid => $role) {
$form['roles_mapped_membership_types'][$rid] = [
'#title' => $role,
'#type' => 'select',
'#options' => $membership_types,
'#default_value' => !empty($roles_membership_types_default_values[$rid]) ? $roles_membership_types_default_values[$rid] : '',
];
}
Little bit of code in the submit handler to get the values from the role form elements, and put it together in the format necessary to map the config.
$values = $form_state->getValues();
if (!empty($values['roles_mapped_membership_types'])) {
foreach ($values['roles_mapped_membership_types'] as $rid => $membership_type) {
$roles_to_membership_type_value[] = ['role' => $rid, 'membership_type' => $membership_type];
}
$form_state->setValue('roles_membership_types', $roles_to_membership_type_value);
}
So eventually this code would work:
// Save plugin settings.
$this->configFactory()
->getEditable('openid_connect.settings.' . $plugin_id)
->set('settings', $subform_state->getValues())
->save();