設定スキーマ/メタデータ
Drupal 8には、YAML設定ファイル用にKwalify(http://www.kuwata-lab.com/kwalify/)に触発されたスキーマ/メタデータ言語のサポートが含まれています。Kwalify自体はRubyで書かれており、形式を少し調整する必要があったため、Kwalifyのすべての詳細が直接適用されるわけではありませんが、かなり近いものです。
チートシート
すぐに理解するための便利な例として、このチートシートを見てください。まだ質問がある場合は、読み続けてください:
![]()
/sites/default/files/config-schema-cheat-sheet1.5.pdf
入門例
システムモジュールには、メンテナンスモードに関連する2つの設定項目があります(サイトが通常の訪問者に対してオフラインに切り替えられているかどうか):
<?php
$config = \Drupal::config('system.maintenance');
$message = $config->get('message');
$langcode = $config->get('langcode');
?>
(メンテナンスが有効かどうかは、設定ではなくstateシステムに保存されます。)
この設定オブジェクトのデフォルト値は、core/modules/system/config/install/system.maintenance.ymlファイルに次のように保存されています:
message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
langcode: en
各モジュールは、必要な数の設定オブジェクトを持つことができます。これらはすべて、モジュールに付属する1つ以上のスキーマファイルで説明されています。システムモジュールの場合、ファイルはcore/modules/system/config/schemaにあります。system.schema.ymlファイルの関連スキーマセクションは次のようになります:
system.maintenance:
type: config_object
label: 'Maintenance mode'
mapping:
message:
type: text
label: 'Message to display when in maintenance mode'
ファイルの最上位キー("system.maintenance")は、.ymlファイルのベース名("system.maintenance.yml")と設定オブジェクト名(config('system.maintenance'))を参照します。ネストされたレベルは、ファイルの内容を説明します。設定スキーマは、config_object(グローバル設定ファイル用)とconfig_entity(エンティティ用)の2種類の設定ファイルタイプを事前定義します。config_objectタイプはcore.data_types.schema.ymlで次のように定義されています:
# 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タイプは、キーと値のペアの基本タイプです。config_objectタイプを使用すると、メンテナンスモード定義はlangcodeと_coreキーを再利用し、メッセージ自体用に別のキーを追加します。system.maintenance定義に戻ると、スキーマラベルのlabel:'Maintenance mode'がスキーマの内容を説明します。次に、実際の要素がmappingキーの下にリストされ、messageキーが定義され、基本タイプからlangcodeと_coreキーを継承します。各要素にはtypeキーとlabelキーがあり、それぞれデータタイプを説明し、データの説明を提供します。ラベルは通常、システム管理者が値を編集できる設定フォームのラベルと同じか類似しています。
コアがサポートするすべてのケースで、.ymlファイルの最上位要素はmappingであり、その要素は下部のmappingリストで説明されています。2つの定義済みのmappingサブタイプconfig_objectまたはconfig_entityのいずれかを使用する必要があります。mapping内の個々の要素は、データの定義方法に応じて任意のタイプにできます。_coreキー自体と_core内のすべてのキーは、Drupalコア用に予約されています。
スキーマファイルは何に使用されますか?
1. 主要なユースケースは多言語サポートのために導入されました。提供される設定内のすべての翻訳可能な文字列を識別するツールが必要です。これにより、独自の設定やデフォルトビュー、追加のユーザーロール、メニュー項目などを送信すると、https://localize.drupal.orgでモジュール/テーマのリリースの一部として翻訳用に提供できます。このユースケースでは、ネストのレベルとタイプで十分です。
2. また、データに基づいて設定の実際の翻訳フォームを提供するためにもスキーマを使用します。このユースケースでは、タイプがますます重要になり、ラベルが決定的になります。コアの設定翻訳モジュールは、スキーマを使用して翻訳フォームを作成し、翻訳を保存します。最も重要な組み込みの翻訳可能なタイプは、単一行テキスト入力用の「label」と、複数行テキスト入力用の「text」です。
3. 設定オブジェクトに何が保存されているかに関する設定スキーマに組み込まれた知識を使用して、設定オブジェクトのデフォルトの永続化実装は、設定オブジェクトの設定スキーマを必要とし、正しいプロパティが特定のタイプでエクスポートされます。設定スキーマを提供する方が良いですが、どうしても提供したくない場合は、設定エンティティの実装でtoArray()メソッドを実装して、タイプの設定エンティティを保存するためのスキーマを不要にすることができます。
4. 設定スキーマは、値を期待されるタイプに自動的にバインドするためにも使用されます。これにより、PHPとWebフォームが一般的に文字列を他のすべてのタイプより優先する一方で、設定の保存時に正しいタイプが使用されることが保証されます。これは重要です。設定をデプロイするとき、差分には実際の変更のみが表示され、偶然のタイプ変更は表示されないようにする必要があるからです。
5. PHPUnitでは、すべての派生TestBaseテストはデフォルトで設定スキーマを厳密に適用します。スキーマファイルが存在しないか無効な場合、これはスキーマエラーを引き起こします。推奨されませんが、テストで次を設定することでスキップできます:
protected $strictConfigSchema = FALSE;
スキーマのデバッグに役立つモジュールについては、https://drupal.org/project/config_inspectorを参照してください。このモジュールは、データとスキーマのさまざまなビューで、不足しているスキーマとスキーマ要素を見つけるのに役立ちます。
スキーマには、モジュールが提供できる他のアイデアもあります。たとえば、一部に基づいてWebサービスインターフェースを作成することなどです。人々が思いつくであろう、私たちが考えもしなかった他のユースケースがある可能性が高いです。
プロパティ
- type:値のタイプ。基本タイプまたは派生タイプにすることができます(以下の例を参照)。
- label:値のユーザーインターフェースラベル。ラベルは対応する設定フォームラベルと一致する必要はありませんが、ラベルを一致させると明瞭さが向上します。
- translatable:特定のタイプを翻訳可能にする。注:以下を使用できます
type: label
の省略形として:
type: string
translatable: true
- nullable:値を空にできるかどうか。設定しない場合、デフォルトになります。
- class:基本タイプにのみ使用され、解析を実装するクラスを割り当てます(以下の例は、システムで定義されたTypedDataと設定タイプのものです)。
- タイプ固有のプロパティ:
- mapping:マッピング内の基本要素をマッピングするために使用されるmappingタイプの値のプロパティ。マッピング内のキーと値のタイプはスキーマで説明する必要があります。マッピングでは文字列キーのみが許可されます。
- sequence:シーケンス内の基本要素を一覧表示するために使用されるsequenceタイプの値のプロパティ。キーは整数または文字列のどちらでもよく、重要ではありません。
メタデータファイルでサポートされるタイプ
前述のように、最も基本的なタイプと、いくつかの興味深い複雑なタイプはcore.data_types.schema.ymlで定義されています。
# 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'
ご覧のとおり、ほとんどの基本データタイプは、TypedData APIの対応物にマッピングされています。この例は、独自のタイプを定義することがいかに簡単かを示しています。タイプを実装するクラスを定義するだけです。残りの2つの(より複雑な)データタイプは、クラス実装に基づいて定義されています:
# 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はキーと値のペアのリストタイプ(「連想配列」または「ハッシュ」)で、各要素は異なるタイプを持つことができます。一方、Sequenceは単純なインデックス付きリスト(「インデックス付き配列」)で、要素は1つのタイプまたは同じ動的タイプ名に基づいています(下記参照)。キーは重要ではありません。つまり、シーケンスとマッピングの主な違いは、シーケンスではキー名とキー数が不明ですが、マッピングではすべてのキーが明示的に定義されていることです。シーケンスは文字列キーを使用できます。
設定スキーマで定義されている他のすべてのタイプ(system.maintenance自体を含む)は、単に他のタイプから継承します。たとえば、「label」「path」「text」「date_format」「color_hex」は文字列として定義されます。これらのタイプを区別することで、スキーマ解析ツールがさまざまな目的でテキストタイプを識別するのに役立ちます。
# 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'
label、text、date_formatタイプも翻訳可能としてマークされていることに注意してください。つまり、コアインターフェース翻訳モジュールはこれらのタイプの要素を識別し、翻訳上書きファイルを作成して、コミュニティまたは管理者が提供するデータベースからの翻訳に基づいてそれらを翻訳します。翻訳可能な文字列は、日付形式でここに示されているように、translation contextキーでコンテキストを取得できることに注意してください。このように、「Y」のような文字列は追加のコンテキスト「PHP date format」を取得するため、翻訳者は「Y」が「Yes」の略ではなく、年のPHP日付形式であることを認識できます。
同様に、メンテナンスモードについて上記で説明した形式を使用して、基本タイプの上に再利用可能な複雑なタイプを定義できます:
# Mail text with subject and body parts.
mail:
type: mapping
label: 'Mail'
mapping:
subject:
type: label
label: 'Subject'
body:
type: text
label: 'Body'
これにより、subjectとbodyがmappingリストにある電子メールテキスト設定用の再利用可能な「mail」タイプが得られます。これは設定キーのスキーマ定義と同じですが、既存の設定キーではない名前を選択するため、他のスキーマ定義と競合しません。この定義に基づいて、「mail」は他の場所でタイプとして使用できます(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'
[....]
最後に、設定ファイルを定義するための2つの重要な複雑なタイプも、core.data_types.schema.ymlで定義されています:
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
動的タイプ参照
上に示したように、単純なタイプでさえ本質的に参照であり、「mail」のような複雑なタイプは、複雑なタイプを参照するためによく使用されます。値のタイプは静的ではなく、データに依存する場合があります。たとえば、さまざまな効果を適用できる画像スタイルや、さまざまなプラグインで構成されるビューなどです。タイプ名の一部としてデータ内のキーを参照して、動的タイプを参照できます。
タイプ内の変数値は[](角括弧)で囲む必要があり、変数値は既知のコンポーネントと組み合わせることができます。参照には3つのタイプがあります:
1. 要素キーへの参照:たとえば、type: 'book.[%key]'。%keyは要素キーに置き換えられます。
2. ネストされたキーへの参照:たとえば、type: 'views.field.[table]-[field]'。タイプは、ネストされた構造内のtableキーとfieldキーの値に基づいて計算されます。
3. 親キーへの参照:たとえば、type: 'views.display.[%parent.display_plugin]'。親のdisplay_pluginキーを使用して要素のタイプを決定します。
プラグインを多用する画像スタイルやビューには、これの豊富な例があります。core/modules/image/config/install/image.style.medium.ymlの画像スタイルの例を考えてみましょう。このYAMLデータ構造があります:
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
ここで、dataキーの構造は、効果のidプロパティで指定される効果タイプによって異なります。したがって、使用されるタイプはデータに依存し、静的に設定できません。異なる設定の画像スタイルは異なる効果を使用します。したがって、タイプ仕様への参照を組み込む必要があります。image.schema.ymlの関連スキーマセクションは次のようになります:
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
これは、すべての画像スタイル(image.style.*)のメタデータを、name、label、effectsキーのマッピングとして定義します。次に、効果自体はシーケンスであり(効果は任意の数にできます)、リスト内の各要素は効果の詳細を含むマッピングです。シーケンスキーは効果のuuidですが、これは重要ではありません。シーケンスはキーを気にしないため、要素のタイプのみを定義します。効果の一般的な値はid、data、weightですが、dataの内容は親のidの値に依存します(上記の例では、「image_scale」が使用される効果の名前です)。したがって、このスキーマがデータに適用されると、image.effect.image_scaleが有効な参照タイプになります。
シーケンス要素のタイプが厳密に1つの要素リストとして定義されている、少し異なるシーケンス定義に遭遇することもあります。この形式は非推奨であり、Drupal 9で削除されます:
deprecated.sequence.definition.format:
type: sequence
sequence:
- type: string
label: 'DO NOT COPY, THIS IS DEPRECATED'
スキーマファイルの命名
スキーマファイルにはグローバルに一意の名前を付ける必要があります。スキーマファイル名が別の拡張機能の名前と一致する場合、ファイルまたは他のファイルが見つからず、不明確なエラーが発生する可能性があります。したがって、スキーマファイルにはモジュール名のプレフィックスを付けることをお勧めします。
スキーマファイルに使用されるコードスタイル
Drupalコアの他の場所で適用される.ymlコードスタイルに従ってください。従うべきアプローチについては、前述の例を参照してください。重要なポイント:
- ファイルの内容を説明する最上位のコメントを含めます。モジュール全体でスキーマファイルが1つしかない場合は、# Schema for the configuration files of the Contact module.のようなコメントで十分です。
- 追加の明瞭さを提供しないコメントは避けてください。comment.settingsのスキーマを定義するセクションの上の「Comment settings」のようなコメントは不要です。いずれにせよ、スキーマ要素には、それらを適切に説明するラベルが必要です。必要な場合にのみコメントを追加してください。
- 文字列に二重引用符を使用しないでください。単一引用符を使用してください。
- 一貫性のために、ラベル値が1つの単語であっても、ラベル値には単一引用符を使用してください。
- キー定義とタイプには引用符を絶対に使用しないでください(Drupalでは、キー名とタイプは定義上文字列であり、スペースを含めるべきではありません)。
- Drupalでは、YAML設定データファイルに含まれる整数値は文字列に変換されるため、単一引用符で囲まれます。
- 少なくとも翻訳が必要な値にはラベルを追加してください(また、それらをラップするコンテナーにも)。デバッグセクションの下で詳しく説明されている設定インスペクターツールを参照して、スキーマからフォームを有用な方法で生成できるかどうかを確認してください。
- インデントレベルに注意してください。YAMLで正しいインデントを使用して目的のスキーマ構造を取得することが重要であるため、これはコードスタイルの要件自体ではありません。
注:通常の.yml設定データファイルスタイルでは、2つ以上の単語が使用される場合にのみ単一引用符を使用する必要があります。これは、.ymlシリアライゼーションが標準的な方法としてそれを実行するため、この標準により設定の変更が簡単になります。設定ファイルのコーディング標準を参照してください。ただし、上記のスキーマの推奨事項はこれとは異なります。スキーマファイルは常に手書きであり、一貫性を確保するためにラベル値の周囲に常に引用符を使用する方が良いためです。
PHP API
\Drupal::service('config.typed')関数を使用して、メタデータに関連する設定を取得できます(たとえば、システムのメンテナンスモードの場合):
$definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
配列構造は次のようになります:
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"
}
上記の親参照セクションで説明したように、medium画像スタイルの最初の効果のデータに関連する型付きデータを取得するためのより複雑な例:
// 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();
上記のように、これはタイプimage.effect.image_scaleになり、次のようなマップ定義を返します:
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"
}
}
}
}
TypedData APIは、要素に対して完全に使用できます。たとえば:
// 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
スキーマベースの設定ナビゲーションと、スキーマベースのフォーム生成のその他のコード例については、https://drupal.org/project/config_inspectorを参照してください。
スキーマのデバッグ
設定インスペクターモジュールは、スキーマをデータと比較し、フォームの生成と変換(存在する場合)がデータに適用されたスキーマでどのように機能するかを確認するためのユーザーインターフェースを提供します。これはスキーマの問題を見つけるために使用できます。スキーマのデバッグにこれを使用する方法のヒントについては、https://drupal.org/node/1910624#comment-7088154を参照してください。
コアの設定変換モジュールは、スキーマの上に実際のユーザーインターフェースを構築し、ユーザーが設定を翻訳できるようにします。このモジュールを使用して、設定が正しく翻訳されているかどうか、翻訳が正しい場所(フロントエンド)に表示され、一部の場所(元の設定を編集できるバックエンドなど)に表示されないかをデバッグできます。
さらに詳しい情報
この形式に到達する前に、さまざまなアプローチと解決策の可能性(さらに多くの副次的な問題も)が議論された何百ものコメントに加えて、#1866610:設定用にKwalifyに触発されたスキーマ形式を提案と#1648930:設定スキーマを提案し、翻訳に使用するを確認してください。(また、#1914366:すべての設定スキーマファイルをスキーマサブディレクトリに移動も確認してください。そこにある理由がわかります)。スキーマシステムがロケールモジュールとどのように統合されているかについては、#1905152:設定スキーマを統合して、提供される設定が翻訳されるようにするも参照してください。コアの#1952394:コア設定翻訳モジュールは、翻訳モジュールが追加された場所です。
#1602106:デフォルト設定ファイルを文書化するは、一般的な設定ymlルールの文書化の始まりです。