Konfigurationsschema / Metadaten
Drupal 8 beinhaltet Unterstützung für ein Schema-/Metadatenformat, das mit Kwalify (http://www.kuwata-lab.com/kwalify/) für YAML-Konfigurationsdateien erstellt wurde. Kwalify selbst ist in Ruby geschrieben, und es wurden einige Anpassungen am Format vorgenommen, daher gelten nicht alle Details von Kwalify direkt, aber es ist ziemlich ähnlich.
Spickzettel
Für ein schnelles Verständnis und einige praktische Beispiele sehen Sie sich diesen Spickzettel an, und lesen Sie weiter, wenn Sie noch Fragen haben:
/sites/default/files/config-schema-cheat-sheet1.5.pdf
Einführendes Beispiel
Das Systemmodul hat zwei Konfigurationswerte, die sich auf den Wartungsmodus beziehen (unabhängig davon, ob die Seite für normale Besucher in den Offline-Modus versetzt wurde):
<?php $config = \Drupal::config('system.maintenance'); $message = $config->get('message'); $langcode = $config->get('langcode'); ?>
(Ob der Wartungsmodus aktiviert ist, wird im State-System gespeichert, nicht in der Konfiguration.)
Die Standardwerte für dieses Konfigurationsobjekt werden in der Datei core/modules/system/config/install/system.maintenance.yml
wie folgt definiert:
message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.' langcode: en
Jedes Modul kann beliebig viele Konfigurationsobjekte haben. Diese werden in einer oder mehreren Schema-Dateien definiert, die mit dem Modul ausgeliefert werden. Im Fall des Systemmoduls befinden sich diese Dateien in core/modules/system/config/schema
. Der relevante Schema-Abschnitt aus der Datei system.schema.yml
sieht so aus:
system.maintenance: type: config_object label: 'Maintenance mode' mapping: message: type: text label: 'Message to display when in maintenance mode'
Der oberste Schlüssel ("system.maintenance") in der Datei bezieht sich auf den Basisnamen der .yml-Datei ("system.maintenance.yml") und den Namen des Konfigurationsobjekts (config('system.maintenance')
). Die verschachtelten Ebenen beschreiben den Inhalt der Datei. Das Konfigurationsschema definiert zwei Arten von Konfigurationsdateien: config_object
für globale Konfigurationsdateien und config_entity
für Entities. Der Typ config_object
ist in core.data_types.schema.yml folgendermaßen definiert:
# Root of a configuration object. _core_config_info: type: mapping mapping: default_config_hash: type: string label: 'Default configuration hash' config_object: type: mapping mapping: langcode: type: string label: 'Language code' _core: type: _core_config_info
Mapping ist der Basistyp für Schlüssel-Wert-Paare. Mit dem Typ config_object
verwendet die Definition des Wartungsmodus die Schlüssel langcode
und _core
wieder und fügt einen weiteren Schlüssel für die Nachricht selbst hinzu. Die Label-Definition label: 'Maintenance mode'
beschreibt den Inhalt des Schemas. Die tatsächlichen Elemente sind unter mapping
aufgelistet, wo message
definiert ist und langcode
und _core
vom Basistyp übernommen werden. Jedes Element hat einen type
- und einen label
-Schlüssel, die den Datentyp beschreiben und eine Beschreibung liefern. Das Label entspricht meist der Beschriftung in der Konfigurationsformular-Oberfläche, wo der Wert durch Administratoren bearbeitet wird.
Bei allen vom Core unterstützten Fällen ist das oberste Element in der .yml-Datei ein Mapping mit den unten angegebenen Schlüsseln. Sie sollten einen der zwei Mapping-Untertypen config_object
oder config_entity
verwenden. Einzelne Elemente im Mapping können je nach Definition verschiedene Typen haben. Der Schlüssel _core
und alle Schlüssel darin sind für den Drupal Core reserviert.
Wozu werden Schema-Dateien verwendet?
- Primär wurden Schema-Dateien für die Mehrsprachigkeitsunterstützung eingeführt. Es wird ein Werkzeug benötigt, um alle übersetzbaren Strings in der gelieferten Konfiguration zu identifizieren. So können Ihre Standardkonfigurationen, Views, zusätzliche Benutzerrollen, Menüpunkte usw. für Übersetzungen auf https://localize.drupal.org bereitgestellt werden. Hierfür reichen die Ebenen und Typen der Verschachtelung aus.
- Schema-Dateien werden ebenfalls verwendet, um reale Übersetzungsformulare für Konfigurationen basierend auf Ihren Daten zu generieren. Dabei werden Typen wichtiger und Labels entscheidend. Das Core-Übersetzungsmodul verwendet Schema-Informationen, um Übersetzungsformulare zu erstellen und Übersetzungen zu speichern. Zwei wichtige eingebaute übersetzbare Typen sind
label
für Einzeilige Texteingaben undtext
für mehrzeilige Texteingaben. - Basierend auf dem Wissen aus Konfigurationsschemata wird die Standardpersistenz-Implementierung für Konfigurationsobjekte von Schemas abhängig, um sicherzustellen, dass die richtigen Eigenschaften mit korrekten Typen exportiert werden. Obwohl es empfohlen wird, Schema-Dateien bereitzustellen, können Sie alternativ die Methode
toArray()
in Ihrer Konfigurations-Entity implementieren, um keine Schemas zu benötigen. - Schema-Dateien werden auch zur automatischen Bindung von Werten an erwartete Typen genutzt. Das stellt sicher, dass beim Speichern der Konfiguration korrekte Datentypen verwendet werden, was wichtig ist, damit bei Konfigurationsänderungen nur echte Änderungen erkannt werden und nicht zufällige Typänderungen.
- In PHPUnit sorgen alle abgeleiteten Tests von TestBase standardmäßig für strikte Einhaltung des Konfigurationsschemas. Das führt zu Schema-Fehlern, wenn Schema-Dateien fehlen oder ungültig sind. Sie können dies deaktivieren, indem Sie in Ihrem Test setzen:
protected $strictConfigSchema = FALSE;
Sehen Sie sich https://drupal.org/project/config_inspector an, ein Modul, das bei der Fehlersuche in Ihren Schemas hilft. Es unterstützt Sie dabei, fehlende Schemas und Schema-Elemente zu finden und zeigt verschiedene Ansichten Ihrer Daten und Schemas.
Es gibt weitere Ideen für Schemas, z.B. die Erstellung von Web-Service-Schnittstellen darauf basierend. Wahrscheinlich gibt es noch weitere Anwendungsmöglichkeiten, die wir noch nicht bedacht haben.
Eigenschaften
- type: Der Typ des Wertes; kann ein Basistyp oder ein abgeleiteter Typ sein (siehe Beispiele unten).
- label: Die UI-Beschriftung für den Wert. Diese muss nicht unbedingt mit dem Formular-Label übereinstimmen, aber ein Übereinstimmen verbessert die Verständlichkeit.
- translatable: Ob der Typ übersetzbar ist. Hinweis: Sie können
type: label
als Abkürzung für
type: string translatable: true
- nullable: Ob der Wert leer sein darf; wenn nicht gesetzt, ist der Wert standardmäßig nicht nullbar.
- class: Nur für Basistypen, um die Klasse für das Parsing zu definieren (Beispiele unten für TypedData und Core-Konfigurationstypen).
- Typ-spezifische Eigenschaften:
- mapping: Eigenschaft für Mapping-Typen, die Basis-Elemente im Mapping beschreiben. Schlüssel und Typen in Mappings müssen im Schema beschrieben sein. Nur String-Schlüssel sind erlaubt.
- sequence: Eigenschaft für Sequenz-Typen, die auflisten, welche Basiselemente in der Sequenz vorkommen. Schlüssel können Integer oder String sein und haben keine Bedeutung.
Unterstützte Typen in Metadaten-Dateien
Wie oben erwähnt, sind die grundlegendsten und einige komplexere Typen in core.data_types.schema.yml definiert:
# Undefined type used by the system to assign to elements at any level where # configuration schema is not defined. Using explicitly has the same effect as # not defining schema, so there is no point in doing that. undefined: label: 'Undefined' class: '\Drupal\Core\Config\Schema\Undefined' # Explicit type to use when no data typing is possible. Instead of using this # type, we strongly suggest you use configuration structures that can be # described with other structural elements of schema, and describe your schema # with those elements. ignore: label: 'Ignore' class: '\Drupal\Core\Config\Schema\Ignore' # Basic scalar data types from typed data. boolean: label: 'Boolean' class: '\Drupal\Core\TypedData\Plugin\DataType\BooleanData' email: label: 'Email' class: '\Drupal\Core\TypedData\Plugin\DataType\Email' integer: label: 'Integer' class: '\Drupal\Core\TypedData\Plugin\DataType\IntegerData' float: label: 'Float' class: '\Drupal\Core\TypedData\Plugin\DataType\FloatData' string: label: 'String' class: '\Drupal\Core\TypedData\Plugin\DataType\StringData' uri: label: 'Uri' class: '\Drupal\Core\TypedData\Plugin\DataType\Uri'
Wie ersichtlich, sind die meisten Grunddatentypen auf entsprechende Typen des TypedData API gemappt. Dieses Beispiel zeigt auch, wie Sie eigene Typen definieren können: Definieren Sie einfach eine Klasse, die den Typ implementiert. Zwei weitere komplexere Typen werden auf Klassenimplementierungen basierend definiert:
# Container data types for lists with known and unknown keys. mapping: label: Mapping class: '\Drupal\Core\Config\Schema\Mapping' definition_class: '\Drupal\Core\TypedData\MapDataDefinition' sequence: label: Sequence class: '\Drupal\Core\Config\Schema\Sequence' definition_class: '\Drupal\Core\TypedData\ListDataDefinition'
Mapping stellt, wie oben gezeigt, einen Schlüssel-Wert-Paar-Typ („assoziatives Array“ oder „Hash“) dar, bei dem jedes Element einen anderen Typ haben kann, während Sequence eine einfache indizierte Liste („Array“) ist, in der alle Elemente vom selben oder einem verwandten dynamischen Typ sind (siehe unten), und Schlüssel keine Bedeutung haben. Der Unterschied zwischen Sequenzen und Mappings liegt darin, dass Sequenzen eine unbekannte Anzahl und unbekannte Schlüssel haben, während Mappings alle Schlüssel explizit definieren. Sequenzen können auch String-Schlüssel enthalten.
Alle weiteren in den Schemas definierten Typen (inklusive system.maintenance selbst) erben von anderen Typen, z.B. sind „label“, „path“, „text“, „date_format“ und „color_hex“ alle als Strings definiert. Diese Unterscheidung hilft Schema-Parsern, Texttypen für verschiedene Zwecke zu erkennen.
# Human readable string that must be plain text and editable with a text field. label: type: string label: 'Label' translatable: true # Internal Drupal path path: type: string label: 'Path' # Human readable string that can contain multiple lines of text or HTML. text: type: string label: 'Text' translatable: true # PHP Date format string that is translatable. date_format: type: string label: 'Date format' translatable: true translation context: 'PHP date format' # HTML color value. color_hex: type: string label: 'Color'
Beachten Sie, dass die Typen label
, text
und date_format
ebenfalls als übersetzbar markiert sind. Das bedeutet, dass das Core-Übersetzungsmodul Elemente dieses Typs erkennt und Übersetzungen aus der Datenbank anzeigt, die von Community oder Administratoren bereitgestellt wurden, indem Übersetzungs-Override-Dateien erzeugt werden. Übersetzbare Strings können einen Übersetzungskontext haben, wie hier für Datumsformate gezeigt. So wissen Übersetzer, dass „Y“ in diesem Zusammenhang ein PHP-Datumsformat ist und nicht etwa ein „Ja“.
Ähnlich können Sie wiederverwendbare komplexe Typen über Basistypen definieren, wie im Beispiel für den Wartungsmodus:
# Mail text with subject and body parts. mail: type: mapping label: 'Mail' mapping: subject: type: label label: 'Subject' body: type: text label: 'Body'
Dies definiert einen wiederverwendbaren Typ „mail“ für E-Mail-Text, bei dem Betreff und Inhalt als Mapping-Liste definiert sind. Dies ist das gleiche wie eine Schema-Definition für einen Konfigurationsschlüssel, nur mit einem anderen Namen, der kein existierender Konfigurationsschlüssel ist, um Konflikte zu vermeiden. Basierend darauf kann „mail“ an anderen Stellen als Typ verwendet werden (wie in der Schema-Definition für E-Mail-Einstellungen eines Benutzer-Moduls in user.schema.yml):
user.mail: type: config_object label: 'Email settings' mapping: cancel_confirm: type: mail label: 'Account cancellation confirmation' password_reset: type: mail label: 'Password recovery' [....]
Abschließend werden zwei wichtige komplexe Typen für Konfigurationsdateien ebenfalls in core.data_types.schema.yml
definiert:
config_object: type: mapping mapping: langcode: type: string label: 'Language code' _core: type: _core_config_info config_entity: type: mapping mapping: uuid: type: string label: 'UUID' langcode: type: string label: 'Language code' status: type: boolean label: 'Status' dependencies: type: config_dependencies label: 'Dependencies' third_party_settings: type: sequence label: 'Third party settings' sequence: type: '[%parent.%parent.%type].third_party.[%key]' _core: type: _core_config_info
Dynamische Typ-Verweise
Wie oben gezeigt, sind selbst einfache Typen im Grunde genommen Referenzen, und komplexe Typen wie „mail“ werden üblicherweise als Verweise auf komplexe Typen genutzt. Manchmal ist der Werttyp nicht statisch, sondern hängt von den Daten ab, z.B. bei Bildstilen mit verschiedenen Effekten oder Views, die aus verschiedenen Plugins bestehen. Sie können Schlüssel in den Daten als Teil des Typnamens verwenden, um dynamische Typen zu referenzieren.
Variablenwerte in Typen müssen in [] (eckigen Klammern) stehen, und Variablenwerte können mit bekannten Komponenten kombiniert werden. Es gibt drei Arten von Verweisen:
- Verweis auf einen Schlüssel eines Elements, z.B.
type: book.[%key]
, wobei%key
durch den Element-Schlüssel ersetzt wird. - Verweis auf einen verschachtelten Schlüssel, z.B.
type: 'views.field.[table]-[field]'
, wobei der Typ basierend auf Werten von Tabellen- und Feldschlüsseln berechnet wird. - Verweis auf einen Elternelement-Schlüssel, z.B.
type: 'views.display.[%parent.display_plugin]'
, wobei der Schlüsseldisplay_plugin
des Elternteils verwendet wird.
Reiche Beispiele hierfür finden sich in Bildstilen und Views, die intensiv Plugins verwenden. Ein Beispiel aus Bildstilen mit der Datei core/modules/image/config/install/image.style.medium.yml zeigt folgende YAML-Datenstruktur:
name: medium label: 'Medium (220x220)' effects: bddf0d06-42f9-4c75-a700-a33cafa25ea0: id: image_scale data: width: 220 height: 220 upscale: true weight: 0 uuid: bddf0d06-42f9-4c75-a700-a33cafa25ea0 langcode: en
Hier hängt die Struktur des Schlüssels vom Typ des Effekts ab, der im id
-Eigenschaft des Effekts angegeben ist. Daher hängt der verwendete Typ von den Daten ab und kann nicht statisch angegeben werden. Unterschiedlich konfigurierte Bildstile verwenden unterschiedliche Effekte. Deshalb muss der Typ dynamisch verlinkt werden. Der zugehörige Schemaabschnitt aus image.schema.yml sieht folgendermaßen aus:
image.style.*: type: config_entity label: 'Image style' mapping: name: type: string label: type: label label: 'Label' effects: type: sequence sequence: type: mapping mapping: id: type: string data: type: image.effect.[%parent.id] weight: type: integer uuid: type: string
Dies definiert Metadaten für alle Bildstile (image.style.*
) als Mapping von Namen, Labels und Effekt-Schlüsseln. Die Effekte selbst sind eine Sequenz (beliebig viele Effekte), wobei jedes Element ein Mapping mit Details des Effekts ist. Der Schlüssel der Sequenz ist die UUID des Effekts, die aber keine Bedeutung hat, da Sequenzen ihre Schlüssel nicht beachten. Daher wird nur der Elementtyp definiert. Allgemeine Werte für Effekte sind id
, data
und weight
. Der Inhalt von data
hängt vom Wert von id
des Elternteils ab (im Beispiel ist image_scale
der Name des Effekts). Somit ist image.effect.image_scale
ein gültiger dynamischer Typ.
Beachten Sie, dass es auch eine leicht abweichende Definition für Sequenzen gibt, bei der der Sequenztyp strikt als eine Liste von Elementtypen definiert wird. Dieses Format ist veraltet und wird in Drupal 9 entfernt:
deprecated.sequence.definition.format: type: sequence sequence: - type: string label: 'DO NOT COPY, THIS IS DEPRECATED'
Namen Ihrer Schema-Dateien
Ihre Schema-Dateien sollten global eindeutige Namen haben. Wenn ein Schema-Dateiname mit dem eines anderen Moduls übereinstimmt, wird Ihre Datei oder die andere nicht gefunden, was zu schwer nachvollziehbaren Fehlern führt. Daher wird empfohlen, Ihre Schema-Dateien mit dem Modulnamen zu präfixen.
Code-Stil für Schema-Dateien
Folgen Sie einfach dem YAML-Code-Stil, der im Drupal-Core an anderen Stellen verwendet wird. Siehe die obigen Beispiele für den empfohlenen Stil. Wichtige Punkte:
- Fügen Sie einen obersten Kommentar hinzu, der erklärt, was die Datei enthält. Wenn Sie nur eine Schema-Datei für Ihr ganzes Modul haben, reicht ein Kommentar wie
# Schema for the configuration files of the Contact module.
. - Vermeiden Sie Kommentare, die nichts zusätzliches erklären, z.B. „Kommentareinstellungen“ über einem Schemaabschnitt für
comment.settings
. Die Schema-Elemente sollten Labels haben, die das gut beschreiben. Kommentieren Sie nur bei Bedarf. - Verwenden Sie keine doppelten Anführungszeichen für Strings, sondern einfache Anführungszeichen.
- Nutzen Sie einfache Anführungszeichen für Label-Werte, selbst wenn es nur ein Wort ist, zur Konsistenz.
- Setzen Sie niemals Anführungszeichen um Definitionen und Typnamen, da diese per Definition Strings ohne Leerzeichen sind.
- Ganze Zahlen in YAML-Konfigurationsdateien werden als Strings gespeichert, daher sollten sie in einfache Anführungszeichen gesetzt werden.
- Fügen Sie Labels mindestens zu den übersetzbaren Werten hinzu (und zu den Container-Elementen, die sie umgeben). Siehe Konfigurationsinspektor unten, um zu prüfen, ob Ihre Schema-basierte Formularerzeugung sinnvoll ist.
- Achten Sie auf korrekte Einrückungen, da YAML darauf basiert, um die Struktur zu bestimmen.
Hinweis: Die übliche Praxis bei YAML-Konfigurationsdateien ist, einfache Anführungszeichen nur bei mehrwortigen Strings zu verwenden, weil die YAML-Serialisierung das so handhabt. Dieser Standard erleichtert das Ändern der Konfiguration. Die obigen Schema-Regeln unterscheiden sich, weil Schema-Dateien von Hand geschrieben werden und deshalb immer konsistent Labels mit Anführungszeichen versehen sein sollten.
PHP API
Sie können die mit Metadaten verknüpfte Konfiguration über den Service \Drupal::service('config.typed')
abrufen (z.B. für den System-Wartungsmodus):
$definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
Die Struktur des Arrays sieht etwa so aus:
array(5) { ["label"]=> string(16) "Maintenance mode" ["class"]=> string(34) "\Drupal\Core\Config\Schema\Mapping" ["definition_class"]=> string(40) "\Drupal\Core\TypedData\MapDataDefinition" ["mapping"]=> array(2) { ["langcode"]=> array(2) { ["type"]=> string(6) "string" ["label"]=> string(13) "Language code" } ["message"]=> array(2) { ["type"]=> string(4) "text" ["label"]=> string(43) "Message to display when in maintenance mode" } } ["type"]=> string(18) "system.maintenance" }
Ein komplexeres Beispiel für typisierte Daten, wie oben beim ersten Effekt des mittleren Bildstils erwähnt:
// Get typed configuration from under the the image.style.medium config // key's effects children. Take the uuid key shown above in the example config // file (corresponding to the first effect in the style) and the data children's elements. $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects.bddf0d06-42f9-4c75-a700-a33cafa25ea0.data')->getDataDefinition();
Dies liefert den Typ image.effect.image_scale (siehe oben) und gibt eine Map-Definition zurück, z.B.:
object(Drupal\Core\TypedData\MapDataDefinition)#1061 (3) { ["mainPropertyName":protected]=> NULL ["propertyDefinitions":protected]=> NULL ["definition":protected]=> array(5) { ["type"]=> string(24) "image.effect.image_scale" ["label"]=> string(11) "Image scale" ["class"]=> string(34) "\Drupal\Core\Config\Schema\Mapping" ["definition_class"]=> string(40) "\Drupal\Core\TypedData\MapDataDefinition" ["mapping"]=> array(3) { ["width"]=> array(2) { ["type"]=> string(7) "integer" ["label"]=> string(5) "Width" } ["height"]=> array(2) { ["type"]=> string(7) "integer" ["label"]=> string(6) "Height" } ["upscale"]=> array(2) { ["type"]=> string(7) "boolean" ["label"]=> string(7) "Upscale" } } } }
Das TypedData API kann vollständig für Elemente genutzt werden, z.B.:
// Get the effects sequence object from the medium image style. $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects'); // $effects represents the sequence keyed by uuids as shown above in the parent reference // example. Use the getValue() TypedData method to retrieve the value. $first_uuid = key($effects->getValue()); // Take the data keys for this first effect. $data = $effects->get($first_uuid)->get('data'); // Examine values and types for width. $data->get('width')->getPluginId(); // will return 'integer' $data->get('width')->getValue(); // will return 220
Weitere Code-Beispiele zum Navigieren und Generieren von schema-basierter Konfiguration und Formularen finden Sie unter https://drupal.org/project/config_inspector
Debuggen Ihres Schemas
Das Config Inspector Modul bietet eine UI zum Vergleichen von Schema und Daten und zeigt, wie Formular-Generierung und -Umwandlung (wenn verfügbar) mit dem Schema und den Daten funktionieren. Das ist nützlich, um Schema-Probleme zu finden. Siehe https://drupal.org/node/1910624#comment-7088154 für Tipps zur Nutzung zur Schema-Debugging.
Das Core-Modul „Configuration Translation“ bietet eine echte Benutzeroberfläche auf Basis der Schemas, mit der Benutzer Konfiguration übersetzen können. Sie können dieses Modul verwenden, um zu prüfen, ob Ihre Konfiguration korrekt übersetzt wird und Übersetzungen an den richtigen Stellen angezeigt werden (Frontend) bzw. nicht an falschen Stellen (Backend, wo Benutzer die Originalkonfiguration bearbeiten).
Weitere Ressourcen
Siehe #1866610: Schema format inspired by Kwalify und #1648930: Schema configuration and translations mit Hunderten von Kommentaren, die verschiedene Ansätze und Lösungen diskutieren, bevor dieses Format entstanden ist. Ebenso #1914366: Move all config schema files to schema subdirectory, warum Dateien dort sind, wo sie sind. Außerdem #1905152: Schema integration for configuration translation und #1952394: Core configuration translation module, das Modul, das Übersetzungen ins Core bringt.
#1602106: Document default config files - Beginn der Dokumentation der YAML-Konfigurationsregeln.
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.