In order for various systems to properly interact with the configuration items, configuration schemas have been introduced. Schemas are a way to define the configuration items and specify what kind of data they store, be it strings, Booleans, integers, and so on. They are, of course, notated in YAML format and go inside the config/schema folder of a module.
There are three main reasons why configuration needs a schema definition:
- Multilingual support: As we will see later, configuration is translatable in Drupal 8. However, in order to know which parts of the configuration are needed to be, or can be, translated, the schema system has been brought in to provide this additional layer. This way, configuration items that ship with contributed modules can get their own translations on the localize.drupal.org website. Moreover, the schema identifies which configuration bits can be translated, and this allows users to provide translations for those in the UI.
- Configuration entities: Configuration entities require schema definitions in order for the proper identification in the persistence layer of the data types that need to be exported with them. Moreover, schemas are used for the validation of configuration entities.
- Typecasting: Configuration schema ensures that the configuration API is able to always typecast properly the values to their right data types.
Let's look at a configuration example provided by Drupal core to see how the schema works, namely the system.mail configuration provided by the System module. Remember in Chapter 3, Logging and Mailing, we talked about how this configuration item controls the mail plugin used for sending out emails? Well, by default, this is what it looks like:
interface:
default: 'php_mail'
It's a very simple multidimensional array. So, if we now look in the system.schema.yml file for the schema definition, we will find the definitions for all the configuration items that come with the System module. The top-level line represents the name of the configuration item, so if we scroll down, we will find system.mail:
system.mail:
type: config_object
label: 'Mail system'
mapping:
interface:
type: sequence
label: 'Interfaces'
sequence:
type: string
label: 'Interface'
If we look past the irony of the schema being five times bigger than the actual configuration, we can get a pretty good understanding of what this configuration item is all about. And more importantly, Drupal itself can too.
We can see that the system.mail configuration is of the config_object type. This is one of the two main types of configurations, the other being config_entity. The label key is used to indicate the human-readable name of this item, whereas the mapping key contains the definition of its individual elements. We can see the interface having the label "Interfaces" and the type sequence. The latter is a specific type that denotes an array in which the keys are not important. Whenever we want the keys to be taken into account, we will use mapping (as it's done at the top level of this schema definition). And since we are looking at a sequence type, the individual items inside it are also defined as a string type with their own label.
Let's now write our own schema definition for the example configuration file we saw before:
my_string: 'Hello!'
my_int: 10
my_boolean: true
my_array:
my_deep_text: 'Yes, hello, is anybody there?!'
If this configuration was found inside a file called my_module.settings.yml, this would be the corresponding schema definition:
my_module.settings:
type: config_object
label: 'Module settings'
mapping:
my_string:
type: string
label: 'My string that can also be of type text if it was longer'
my_boolean:
type: boolean
label: 'My boolean'
my_array:
type: mapping
label: 'My array in which the keys are also important, hence not a sequence'
mapping:
my_deep_text:
type: text
label: 'My hello string'
As a bonus piece of information, any config_object-typed configuration inherits the following property:
langcode:
type: string
label: 'Language code'
This helps with the multilingual system and invites us to add a langcode property to each configuration item.
Most of the properties we've seen so far have been type, label, mapping, and sequence. There are two more that you should be aware of:
- translatable : very important as it indicates whether a type can be translated. By default, text and label types are already set to translatable, so you don't need to do so yourself.
- nullable : indicates whether the value can be left empty. If missing, it's considered as being required.
Here are some types you can use to define configuration:
- Scalar types: string, integer, boolean, email, float, uri, path
- Lists: mapping, sequence
- Complex (extending scalar types): label, path, text, date_format and more.
Make sure you check out the core.data_types.schema.yml file where all of these are defined.
Before we move on, let's make sure we create the configuration schema for our configuration item we created programmatically in Chapter 2, Creating Your First Module, namely the one storing the overridden salutation message. So, inside the /config/schema folder of the Hello World module, we can have the hello_world.schema.yml file with the following:
hello_world.custom_salutation:
type: config_object
label: 'Salutation settings'
mapping:
salutation:
type: string
label: 'The salutation message'
That takes care of some technical debt we introduced back when we didn't know about configuration schemas.