Defining a proper Schema for a custom field Type in Drupal 8+ doesn't seem to be very well documented yet. Luckily, the core gives us some good examples to start from.
For that reason, you should have a look into the core modules that provide custom field types and especially into their config/schema yml.
These are, for example (and with different use-cases and complexity):
- Comment
- Datetime
- Image
- Link
- Responsive Image
- ...
In my example I'll use /web/core/modules/link/config/schema/link.schema.yml because it's one of the easier ones to understand.
The relevant part looks like this (as of Drupal 10.2):
# Schema for the configuration files of the Link module.
field.formatter.settings.link:
type: mapping
label: 'Link format settings'
mapping:
trim_length:
type: integer
label: 'Trim link text length'
url_only:
type: boolean
label: 'URL only'
url_plain:
type: boolean
label: 'Show URL as plain text'
rel:
type: string
label: 'Add rel="nofollow" to links'
target:
type: string
label: 'Open link in new window'
field.formatter.settings.link_separate:
type: field.formatter.settings.link
label: 'Link format settings'
field.widget.settings.link_default:
type: mapping
label: 'Link format settings'
mapping:
placeholder_url:
type: string
label: 'Placeholder for URL'
placeholder_title:
type: label
label: 'Placeholder for link text'
field.storage_settings.link:
type: mapping
label: 'Link settings'
field.field_settings.link:
type: mapping
label: 'Link settings'
mapping:
title:
type: integer
label: 'Allow link text'
link_type:
type: integer
label: 'Allowed link type'
field.value.link:
type: mapping
label: 'Default value'
mapping:
attributes:
type: mapping
label: 'Link attributes'
mapping:
title:
type: label
label: 'Link text'
uri:
type: string
label: 'URL'
title:
type: label
label: 'Link text'
options:
type: mapping
label: 'Link options'
mapping:
query:
type: sequence
label: 'URL query key value pairs'
sequence:
type: string
fragment:
type: string
label: 'URL fragment'
absolute:
type: boolean
label: 'Whether to force the output to be an absolute link (beginning with http: or https:)'
https:
type: boolean
label: 'Whether to force this URL to point to a secure location (beginning with https:)'
attributes:
type: sequence
label: 'Link attributes'
sequence:
type: string
Let's split this into pieces for explanation.
The id "link" is from the annotation of the field / widget / formatter, for example:
/**
* Plugin implementation of the 'link' field type.
*
* @FieldType(
* id = "link",
* [...]
so you use the id from the ones you defined.
- field.formatter.settings.link: Per field formatter settings schema. The link module for example provides two formatters:
- field.formatter.settings.link
- field.formatter.settings.link_separate
- field.widget.settings.link_default: Per field widget settings schema. The link module only provides one widget
- field.storage_settings.link: Per field storage settings schema. These settings are global per field, so they have the same value for all field instances if the field is reused. (A field with the same machine name can only exist once (storage) per entity type, but may be reused)
- field.field_settings.link: Per field settings schema. These settings are individual per (reused) field instance. In link fields they for example allow to configure to enter a link text or a link type.
- field.value.link: This is (kind of) the field (default) value schema. And it's a bit strange: The field value schema (data schema) is already defined in public static function propertyDefinitions for the database schema. You have to define the same again in the schema.yml file because the field value defaults are stored in config. And that's what the schema is for!