Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
202 lines
13 KiB
Plaintext
202 lines
13 KiB
Plaintext
---
|
||
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
|
||
```
|
||
|
||
### 解決策
|
||
|
||
<Note>
|
||
`getFieldUniversalIdentifier` は `twenty-sdk` 2.21 以降で利用可能です。
|
||
</Note>
|
||
|
||
`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` が必要とされるあらゆる場所、すなわちビューのフィールド、フィルター、ソート、グループ、ページレイアウトウィジェットでそのまま使用できます。
|
||
|
||
<Note>
|
||
id は解決して、ハードコードしないでください。 サーバーはその値をアプリケーション id、オブジェクト id、フィールド名から導出するため、`getFieldUniversalIdentifier` を呼び出すことで、それらの入力が変更された場合でも参照を正しい状態に保ち、導出方法が将来変わった場合のずれも防ぐことができます。
|
||
</Note>
|
||
|
||
### システムリレーションフィールド
|
||
|
||
<Note>
|
||
`getSystemRelationFieldUniversalIdentifier` は `twenty-sdk`
|
||
2.23 以降で利用可能であり、バージョン 2.23 以降の Twenty サーバーが必要です。
|
||
</Note>
|
||
|
||
上記のスカラー型システムフィールドに加えて、サーバーはすべてのオブジェクトに対して 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` が想定されるあらゆる場所で使用できます。
|
||
|
||
## システムビュー
|
||
|
||
<Note>
|
||
`getSystemViewUniversalIdentifier` と `getSystemViewFieldUniversalIdentifier` は `twenty-sdk` 2.26 以降で利用可能であり、バージョン 2.26 以降の Twenty サーバーが必要です。
|
||
</Note>
|
||
|
||
サーバーは、すべてのオブジェクトに対して **システムビュー** もプロビジョニングします。つまり、表示可能なフィールドごとに 1 つのカラムを持つメインのリストビュー(`All {objectLabelPlural}`、キーは `ViewKey.INDEX`(`SYSTEM_VIEW_KEYS.INDEX` から導出))です。 システムリレーションフィールドと同様に、それらの識別子は **名前に依存しない** 形で導出されるため、オブジェクトやフィールドの名前を変更しても識別子は変わりません。
|
||
|
||
ビューを解決するには、`getSystemViewUniversalIdentifier` を使用します。
|
||
|
||
```ts
|
||
import {
|
||
getSystemViewUniversalIdentifier,
|
||
SYSTEM_VIEW_KEYS,
|
||
} from 'twenty-sdk/define';
|
||
|
||
const rocketIndexViewId = getSystemViewUniversalIdentifier({
|
||
objectMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||
objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER,
|
||
viewKey: SYSTEM_VIEW_KEYS.INDEX,
|
||
});
|
||
```
|
||
|
||
* `objectMetadataApplicationUniversalIdentifier` は **オブジェクト** を所有しているアプリケーションであり、ビューが名前空間分割されている単位です。
|
||
* `objectUniversalIdentifier` は、そのビューがリストするオブジェクトです。
|
||
* `viewKey` はシステムビューを識別するための判別子です。メインのリストビューには `SYSTEM_VIEW_KEYS.INDEX`、レコードページのフィールドウィジェットのビューには `SYSTEM_VIEW_KEYS.FIELDS_WIDGET` を使用します。 これは導出のキーとなる値であり、ビューの行に保存されるのは `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 が所有するビュー上で、そのアプリケーションの名前空間の下にカラムが導出されます。
|
||
|
||
<Warning>
|
||
システムビューとそのカラムは **サーバー所有** です。これらの識別子は、宣言ではなく参照のためにのみ解決してください。 [`defineView()`](/l/ja/developers/extend/apps/layout/views) の `key` は非推奨で無視されます。そのため、マニフェストのビューが `INDEX` キーを主張することはできません。また、サーバーはあなたが追加するすべてのフィールドに対してすでにカラムをプロビジョニングしているため、同じフィールドについてシステムビュー上で独自の `defineViewField()` を宣言すると競合が発生します。
|
||
</Warning>
|
||
|
||
## 標準の 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) で **あなたのアプリ** が定義するオブジェクトのように、そのような定数が存在しない場合には、ここまでで紹介したヘルパーを使用してください。
|
||
|
||
<Note>
|
||
`name` はシステムフィールドではなく、**デフォルト** フィールドです。 このフィールドは独自のハードコードされたユニバーサル識別子を持ち、`getFieldUniversalIdentifier` で解決されることはありません。 あなたが定義したオブジェクトでは、`name` フィールドは `defineObject()` で与えた識別子で参照してください。
|
||
</Note>
|