--- title: システムメタデータのターゲット指定 description: Twenty がすべてのオブジェクトに自動的にプロビジョニングするメタデータの決定論的なユニバーサル識別子を解決し、アプリがハードコーディングせずに参照できるようにします。 icon: gears --- Twenty のすべてのオブジェクトには、フィールドのセットや、そのカラムを持つメインのリストビューなど、あなた自身が宣言する必要のない **システムメタデータ** が付属しています。 オブジェクトがプロビジョニングされるときに、そのすべてをサーバーが作成し、そのセットは Twenty の拡張に合わせて増えていきます。 あなたがそれを宣言しないため、インポートできる `universalIdentifier` 定数は存在しません。 その代わりに、サーバーはそれぞれの識別子を決定論的に**導出**し、`twenty-sdk` が同じ導出処理を公開することで、マニフェストがサーバーで使用される正確な値を解決できるようにします。 ## システムフィールド すべてのオブジェクトに存在し、いずれも [`defineField()`](/l/ja/developers/extend/apps/data/extending-objects) で宣言しないスカラー フィールドは次のとおりです。 `id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector` では、[view](/l/ja/developers/extend/apps/layout/views) で `createdAt` をカラムとしてどのように参照すればよいのでしょうか。 ### 問題 Twenty 2.19 以降、システムフィールドのユニバーサル識別子は、アプリケーションのユニバーサル識別子、オブジェクトのユニバーサル識別子、およびフィールド名という 3 つの入力からサーバーによって **決定的に導出** されます。 id を独自に作成してハードコードしても機能しません。サーバー上のどの値とも一致せず、同期時にそのぶら下がった参照は拒否されます。 ``` Dev sync failed: viewField: INVALID_VIEW_DATA: Field metadata not found ``` ### 解決策 `getFieldUniversalIdentifier` は `twenty-sdk` 2.21 以降で利用可能です。 `getFieldUniversalIdentifier` を使用して、サーバーが使用するものとまったく同じ値を解決します。 この関数は 3 つの入力を受け取り、フィールドのユニバーサル識別子を返します。 ```ts import { getFieldUniversalIdentifier } from 'twenty-sdk/define'; const createdAtFieldId = getFieldUniversalIdentifier({ applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER, name: 'createdAt', }); ``` * `applicationUniversalIdentifier` はあなたのアプリの識別子であり、[`defineApplication()`](/l/ja/developers/extend/apps/config/application) に渡すものです。 * `objectUniversalIdentifier` は、そのフィールドが属するオブジェクトの識別子です。 * `name` はシステムフィールド名であり、上に挙げた値のいずれかです。 ### 例: ビュー内の createdAt カラム 典型的なケースは、カスタムオブジェクトの 1 つのビューに `createdAt` カラムを追加することです。 フィールド id を解決し、他の `fieldMetadataUniversalIdentifier` と同様にそれを参照します。 ```ts src/views/example-view.ts import { defineView, getFieldUniversalIdentifier, } from 'twenty-sdk/define'; const APPLICATION_UNIVERSAL_IDENTIFIER = '0b04e15c-27b2-4741-9046-b32e07469072'; const MY_OBJECT_UNIVERSAL_IDENTIFIER = 'c782b61c-70fd-4c88-9cd6-4e61ab8d7591'; export default defineView({ universalIdentifier: '70f10d44-144a-4da8-8c6f-3ec2422138c0', name: 'All records', objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER, icon: 'IconList', position: 0, fields: [ { universalIdentifier: '75a90bc4-d901-4df4-85e0-af29db5e0104', fieldMetadataUniversalIdentifier: getFieldUniversalIdentifier({ applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER, name: 'createdAt', }), position: 0, isVisible: true, size: 200, }, ], }); ``` 同じように解決された id は、`fieldMetadataUniversalIdentifier` が必要とされるあらゆる場所、すなわちビューのフィールド、フィルター、ソート、グループ、ページレイアウトウィジェットでそのまま使用できます。 id は解決して、ハードコードしないでください。 サーバーはその値をアプリケーション id、オブジェクト id、フィールド名から導出するため、`getFieldUniversalIdentifier` を呼び出すことで、それらの入力が変更された場合でも参照を正しい状態に保ち、導出方法が将来変わった場合のずれも防ぐことができます。 ### システムリレーションフィールド `getSystemRelationFieldUniversalIdentifier` は `twenty-sdk` 2.23 以降で利用可能であり、バージョン 2.23 以降の Twenty サーバーが必要です。 上記のスカラー型システムフィールドに加えて、サーバーはすべてのオブジェクトに対して 4 つの**システムリレーションフィールド**を提供します。`timelineActivities`、`attachments`、`noteTargets`、`taskTargets` であり、それぞれが対応する標準リレーションオブジェクトを参照します。 これらのフィールドは `getFieldUniversalIdentifier` では解決されません。フィールドを保持しているオブジェクトと、そのフィールドが参照するオブジェクトから、**名前に依存せず**識別子が導出されます。 この方法により、オブジェクトの名称を変更しても、そのリレーションフィールドの識別子は決して変わりません。 それらを解決するには、`getSystemRelationFieldUniversalIdentifier` を使用します。 ```ts import { getSystemRelationFieldUniversalIdentifier, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, } from 'twenty-sdk/define'; // rocket.attachments — the relation field hosted on your custom object const rocketAttachmentsFieldId = getSystemRelationFieldUniversalIdentifier({ applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER, relationTargetObjectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier, }); ``` * `objectUniversalIdentifier` は、そのフィールドを **保持している** オブジェクトです。 * `relationTargetObjectUniversalIdentifier` は、そのフィールドが **参照している** オブジェクトです。 方向は引数の順序によってエンコードされます。 逆方向(例: `attachment.targetRocket`。これは、標準リレーションオブジェクト上でサーバーが作成する morph フィールド)を解決するには、2 つを入れ替えます。 ```ts // attachment.targetRocket — the reverse morph field on Attachment const attachmentTargetRocketFieldId = getSystemRelationFieldUniversalIdentifier({ applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier, relationTargetObjectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER, }); ``` スカラーのシステムフィールドと同様に、解決された ID は `fieldMetadataUniversalIdentifier` が想定されるあらゆる場所で使用できます。 ## システムビュー `getSystemViewUniversalIdentifier` と `getSystemViewFieldUniversalIdentifier` は `twenty-sdk` 2.26 以降で利用可能であり、バージョン 2.26 以降の Twenty サーバーが必要です。 サーバーは、すべてのオブジェクトに対して **システムビュー** もプロビジョニングします。つまり、表示可能なフィールドごとに 1 つのカラムを持つメインのリストビュー(`ViewKey.INDEX` をキーとし、`All {objectLabelPlural}`)です。 システムリレーションフィールドと同様に、それらの識別子は **名前に依存しない** 形で導出されるため、オブジェクトやフィールドの名前を変更しても識別子は変わりません。 ビューを解決するには、`getSystemViewUniversalIdentifier` を使用します。 ```ts import { getSystemViewUniversalIdentifier, ViewKey } from 'twenty-sdk/define'; const rocketIndexViewId = getSystemViewUniversalIdentifier({ objectMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER, viewKey: ViewKey.INDEX, }); ``` * `objectMetadataApplicationUniversalIdentifier` は **オブジェクト** を所有しているアプリケーションであり、ビューが名前空間分割されている単位です。 * `objectUniversalIdentifier` は、そのビューがリストするオブジェクトです。 * `viewKey` はシステムビューのキーで、現時点では `ViewKey.INDEX` です。 解決された ID は、[`NavigationMenuItemType.VIEW`](/l/ja/developers/extend/apps/layout/navigation-menu-items) のサイドバーエントリなど、`viewUniversalIdentifier` が必要とされるあらゆる場所で使用できます。 オブジェクトのメインリストを単に開くだけであれば、導出が不要な `NavigationMenuItemType.OBJECT` と `targetObjectUniversalIdentifier` を優先して使用してください。 `getSystemViewFieldUniversalIdentifier` は、ビューとそのビューが表示するフィールドから、システムビュー上の 1 つの**カラム**を解決します。 ```ts import { getSystemViewFieldUniversalIdentifier } from 'twenty-sdk/define'; const rocketNameColumnId = getSystemViewFieldUniversalIdentifier({ fieldMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, viewUniversalIdentifier: rocketIndexViewId, fieldMetadataUniversalIdentifier: ROCKET_NAME_FIELD_UNIVERSAL_IDENTIFIER, }); ``` 最初の引数に注目してください。カラムは、そのビューではなく、**表示しているフィールド** を所有するアプリケーションで名前空間分割されます。 あなたのアプリが標準オブジェクトに追加したフィールドは、Twenty が所有するビュー上で、そのアプリケーションの名前空間の下にカラムが導出されます。 システムビューとそのカラムは **サーバー所有** です。これらの識別子は、宣言ではなく参照のためにのみ解決してください。 [`defineView()`](/l/ja/developers/extend/apps/layout/views) の `key` は非推奨で無視されます。そのため、マニフェストのビューが `INDEX` キーを主張することはできません。また、サーバーはあなたが追加するすべてのフィールドに対してすでにカラムをプロビジョニングしているため、同じフィールドについてシステムビュー上で独自の `defineViewField()` を宣言すると競合が発生します。 ## 標準の Twenty オブジェクト **標準** の Twenty オブジェクト(Person、Company、Opportunity など)の場合、何も導出する必要はありません。フィールドおよびビューの識別子は、あらかじめ計算された定数として用意されており、直接インポートできます。 ```ts import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define'; // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.createdAt.universalIdentifier // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.updatedAt.universalIdentifier // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.views.allPeople.universalIdentifier ``` [`defineObject()`](/l/ja/developers/extend/apps/data/objects) で **あなたのアプリ** が定義するオブジェクトのように、そのような定数が存在しない場合には、ここまでで紹介したヘルパーを使用してください。 `name` はシステムフィールドではなく、**デフォルト** フィールドです。 このフィールドは独自のハードコードされたユニバーサル識別子を持ち、`getFieldUniversalIdentifier` で解決されることはありません。 あなたが定義したオブジェクトでは、`name` フィールドは `defineObject()` で与えた識別子で参照してください。