--- title: 関連 description: 双方向の MANY_TO_ONE / ONE_TO_MANY リレーションでオブジェクト同士を接続します。 icon: diagram-project --- リレーションは 2 つのオブジェクト同士を関連付けます。 Twenty におけるリレーションは常に **双方向** です — すべてのリレーションには 2 つの側面があり、それぞれが相手を参照するフィールドとして宣言されます。 | リレーションタイプ | 説明 | 外部キーあり? | | ------------- | --------------------------------- | -------------------- | | `MANY_TO_ONE` | このオブジェクトの多数のレコードが、対象の 1 件のレコードを指す | はい(`joinColumnName`) | | `ONE_TO_MANY` | このオブジェクトの 1 件のレコードが、対象の多数のレコードを持つ | いいえ(逆側) | ## リレーションの仕組み すべてのリレーションには、互いを参照する**2 つのフィールド**が必要です: 1. **MANY_TO_ONE** 側 — 外部キーを保持するオブジェクト側に存在します。 2. **ONE_TO_MANY** 側 — コレクションを所有するオブジェクト側に存在します。 両方のフィールドは `FieldType.RELATION` を使用し、`relationTargetFieldMetadataUniversalIdentifier` を介して相互参照します。 ## 例: PostCard は多数の Recipient を持つ `PostCard` は多数の `PostCardRecipient` レコードに送信できます。 各受取人は厳密に 1 件の PostCard に属します。 **ステップ 1: PostCard に ONE_TO_MANY 側を定義**(「1」の側): ```ts src/fields/post-card-recipients-on-post-card.field.ts import { defineField, FieldType, RelationType } from 'twenty-sdk/define'; import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; // Export so the other side can reference it export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111'; // Import from the other side import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field'; export default defineField({ universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, type: FieldType.RELATION, name: 'postCardRecipients', label: 'Post Card Recipients', icon: 'IconUsers', relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID, universalSettings: { relationType: RelationType.ONE_TO_MANY, }, }); ``` **ステップ 2: PostCardRecipient に MANY_TO_ONE 側を定義**(「多」の側 — 外部キーを保持): ```ts src/fields/post-card-on-post-card-recipient.field.ts import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define'; import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; // Export so the other side can reference it export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222'; // Import from the other side import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field'; export default defineField({ universalIdentifier: POST_CARD_FIELD_ID, objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, type: FieldType.RELATION, name: 'postCard', label: 'Post Card', icon: 'IconMail', relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, universalSettings: { relationType: RelationType.MANY_TO_ONE, onDelete: OnDeleteAction.CASCADE, joinColumnName: 'postCardId', }, }); ``` **循環インポート:** 両方のリレーションフィールドは互いの `universalIdentifier` を参照します。 循環インポートの問題を避けるため、各ファイルからフィールド ID を名前付き定数としてエクスポートし、相手のファイルでインポートしてください。 ビルドシステムがコンパイル時にこれらを解決します。 ## 標準オブジェクトとのリレーション 組み込みの Twenty オブジェクト(Person、Company など)とリレーションを作成するには、`STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` を使用します: ```ts src/fields/person-on-self-hosting-user.field.ts import { defineField, FieldType, RelationType, OnDeleteAction, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, } from 'twenty-sdk/define'; import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object'; export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333'; export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444'; export default defineField({ universalIdentifier: PERSON_FIELD_ID, objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER, type: FieldType.RELATION, name: 'person', label: 'Person', description: 'Person matching with the self hosting user', isNullable: true, relationTargetObjectMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID, universalSettings: { relationType: RelationType.MANY_TO_ONE, onDelete: OnDeleteAction.SET_NULL, joinColumnName: 'personId', }, }); ``` ## リレーションフィールドのプロパティ | プロパティ | 必須 | 説明 | | ------------------------------------------------- | -------------- | ------------------------------------------------------------------- | | `type` | はい | `FieldType.RELATION` でなければなりません | | `relationTargetObjectMetadataUniversalIdentifier` | はい | 対象オブジェクトの `universalIdentifier` | | `relationTargetFieldMetadataUniversalIdentifier` | はい | 対象オブジェクト上の対応するフィールドの `universalIdentifier` | | `universalSettings.relationType` | はい | `RelationType.MANY_TO_ONE` または `RelationType.ONE_TO_MANY` | | `universalSettings.onDelete` | MANY_TO_ONE のみ | 参照先レコードが削除されたときの動作: `CASCADE`、`SET_NULL`、`RESTRICT`、または `NO_ACTION` | | `universalSettings.joinColumnName` | MANY_TO_ONE のみ | 外部キーのデータベース列名(例: `postCardId`) | ## インラインのリレーションフィールド [`defineObject`](/l/ja/developers/extend/apps/data/objects) の中で、リレーションを直接宣言することもできます。 インラインで宣言する場合は、`objectUniversalIdentifier` を省略します — 親オブジェクトから継承されます: ```ts export default defineObject({ universalIdentifier: '...', nameSingular: 'postCardRecipient', // ... fields: [ { universalIdentifier: POST_CARD_FIELD_ID, type: FieldType.RELATION, name: 'postCard', label: 'Post Card', relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, universalSettings: { relationType: RelationType.MANY_TO_ONE, onDelete: OnDeleteAction.CASCADE, joinColumnName: 'postCardId', }, }, // … other fields ], }); ```