function Mapping::getDynamicallyValidKeys

Same name and namespace in other branches
  1. 10 core/lib/Drupal/Core/Config/Schema/Mapping.php \Drupal\Core\Config\Schema\Mapping::getDynamicallyValidKeys()

Gets all dynamically valid keys.

When the `type` of the mapping is dynamic itself. For example: the settings associated with a FieldConfig depend on what kind of field it is (i.e., which field plugin it uses).

Other examples:

  • CKEditor 5 uses 'ckeditor5.plugin.[%key]'; the mapping is stored in a sequence, and `[%key]` is replaced by the mapping's key in that sequence.
  • third party settings use '[%parent.%parent.%type].third_party.[%key]'; `[%parent.%parent.%type]` is replaced by the type of the mapping two levels up. For example, 'node.type.third_party.[%key]'.
  • field instances' default values have a type of 'field.value.[%parent.%parent.field_type]'. This uses the value of the `field_type` key from the mapping two levels up.
  • Views filters have a type of 'views.filter.[plugin_id]'; `[plugin_id]` is replaced by the value of the mapping's `plugin_id` key.

In each of these examples, the mapping may have keys that are dynamically valid, meaning that which keys are considered valid may depend on other values in the tree.

Return value

string[][] A list of dynamically valid keys. An array with:

  • a key for every possible resolved type
  • the corresponding value an array of the additional mapping keys that are supported for this resolved type

See also

\Drupal\Core\Config\TypedConfigManager::resolveDynamicTypeName()

\Drupal\Core\Config\TypedConfigManager::resolveExpression()

https://www.drupal.org/files/ConfigSchemaCheatSheet2.0.pdf

File

core/lib/Drupal/Core/Config/Schema/Mapping.php, line 126

Class

Mapping
Defines a mapping configuration element.

Namespace

Drupal\Core\Config\Schema

Code

public function getDynamicallyValidKeys() : array {
    $parent_data_def = $this->getParent()?->getDataDefinition();
    if ($parent_data_def === NULL) {
        return [];
    }
    // Use the parent data definition to determine the type of this mapping
    // (including the dynamic placeholders). For example:
    // - `editor.settings.[%parent.editor]`
    // - `editor.image_upload_settings.[status]`.
    $original_mapping_type = match (TRUE) {    $parent_data_def instanceof MapDataDefinition => $parent_data_def->toArray()['mapping'][$this->getName()]['type'],
        $parent_data_def instanceof SequenceDataDefinition => $parent_data_def->toArray()['sequence']['type'],
        default => throw new \LogicException('Invalid config schema detected.'),
    
    };
    // If this mapping's type isn't dynamic, there's nothing to do.
    if (!str_contains($original_mapping_type, ']')) {
        return [];
    }
    elseif (str_starts_with($original_mapping_type, '[')) {
        return [];
    }
    // Expand the dynamic placeholders to find all mapping types derived from
    // the original mapping type. To continue the previous example:
    // - `editor.settings.unicorn`
    // - `editor.image_upload_settings.*`
    // - `editor.image_upload_settings.1`
    $possible_types = $this->getPossibleTypes($original_mapping_type);
    // TRICKY: it is tempting to not consider this a dynamic type if only one
    // concrete type exists. But that would lead to different validation errors
    // when modules are installed or uninstalled.
    assert(!empty($possible_types));
    // Determine all valid keys, across all possible types.
    $typed_data_manager = $this->getTypedDataManager();
    $all_type_definitions = $typed_data_manager->getDefinitions();
    $possible_type_definitions = array_intersect_key($all_type_definitions, array_fill_keys($possible_types, TRUE));
    // TRICKY: \Drupal\Core\Config\TypedConfigManager::getDefinition() does the
    // necessary resolving, but TypedConfigManager::getDefinitions() does not! 🤷‍♂️
    // @see \Drupal\Core\Config\TypedConfigManager::getDefinitionWithReplacements()
    // @see ::getValidKeys()
    $valid_keys_per_type = [];
    foreach (array_keys($possible_type_definitions) as $possible_type_name) {
        $valid_keys_per_type[$possible_type_name] = array_keys($typed_data_manager->getDefinition($possible_type_name)['mapping'] ?? []);
    }
    // From all valid keys across all types, get the ones for the fallback type:
    // its keys are inherited by all type definitions and are therefore always
    // ("statically") valid. Not all types have a fallback type.
    // @see \Drupal\Core\Config\TypedConfigManager::getDefinitionWithReplacements()
    $fallback_type = $typed_data_manager->findFallback($original_mapping_type);
    $valid_keys_everywhere = array_intersect_key($valid_keys_per_type, [
        $fallback_type => NULL,
    ]);
    assert(count($valid_keys_everywhere) <= 1);
    $statically_required_keys = NestedArray::mergeDeepArray($valid_keys_everywhere);
    // Now that statically valid keys are known, determine which valid keys are
    // only valid in *some* cases: remove the statically valid keys from every
    // per-type array of valid keys.
    $valid_keys_some = array_diff_key($valid_keys_per_type, $valid_keys_everywhere);
    $valid_keys_some_processed = array_map(fn(array $keys) => array_values(array_filter($keys, fn(string $key) => !in_array($key, $statically_required_keys, TRUE))), $valid_keys_some);
    return $valid_keys_some_processed;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.