Passer au contenu principal
Les relations relient deux objets entre eux. Dans Twenty, les relations sont toujours bidirectionnelles — chaque relation a deux côtés, et chaque côté est déclaré comme un champ qui référence l’autre.
Type de relationDescriptionPossède une clé étrangère ?
MANY_TO_ONEDe nombreux enregistrements de cet objet pointent vers un enregistrement de la cibleOui (joinColumnName)
ONE_TO_MANYUn enregistrement de cet objet possède de nombreux enregistrements de la cibleNon (le côté inverse)

Fonctionnement des relations

Chaque relation nécessite deux champs qui se référencent mutuellement :
  1. Le côté MANY_TO_ONE — réside sur l’objet qui détient la clé étrangère.
  2. Le côté ONE_TO_MANY — réside sur l’objet qui possède la collection.
Les deux champs utilisent FieldType.RELATION et se référencent mutuellement via relationTargetFieldMetadataUniversalIdentifier.

Exemple : une carte postale a de nombreux destinataires

Un PostCard peut être envoyé à de nombreux enregistrements PostCardRecipient. Chaque destinataire appartient à exactement une carte postale. Étape 1: Définir le côté ONE_TO_MANY sur PostCard (le côté “one”) :
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,
  },
});
Étape 2: Définir le côté MANY_TO_ONE sur PostCardRecipient (le côté “many” — détient la clé étrangère) :
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',
  },
});
Imports circulaires : les deux champs de relation se référencent l’un l’autre via leur universalIdentifier. Pour éviter les problèmes d’import circulaire, exportez les ID de vos champs en tant que constantes nommées depuis chaque fichier, puis importez-les dans l’autre. Le système de build les résout au moment de la compilation.

Relation avec des objets standard

Pour créer une relation avec un objet Twenty intégré (Person, Company, etc.), utilisez STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS :
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',
  },
});

Propriétés des champs de relation

PropriétéObligatoireDescription
typeOuiDoit être FieldType.RELATION
relationTargetObjectMetadataUniversalIdentifierOuiLe universalIdentifier de l’objet cible
relationTargetFieldMetadataUniversalIdentifierOuiLe universalIdentifier du champ correspondant sur l’objet cible
universalSettings.relationTypeOuiRelationType.MANY_TO_ONE ou RelationType.ONE_TO_MANY
universalSettings.onDeleteMANY_TO_ONE uniquementCe qui se passe lorsque l’enregistrement référencé est supprimé : CASCADE, SET_NULL, RESTRICT ou NO_ACTION
universalSettings.joinColumnNameMANY_TO_ONE uniquementNom de la colonne de base de données pour la clé étrangère (p. ex., postCardId)

Champs de relation en ligne

Vous pouvez également déclarer une relation directement à l’intérieur de defineObject. Lorsqu’il est en ligne, omettez objectUniversalIdentifier — il est hérité de l’objet parent :
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
  ],
});