الانتقال إلى المحتوى الرئيسي
تربط العلاقات كائنين معًا. في Twenty، تكون العلاقات دائمًا ثنائية الاتجاه — لكل علاقة جانبَان، ويُصرَّح عن كل جانب كحقل يُشير إلى الآخر.
نوع العلاقةالوصفهل لديه مفتاح خارجي؟
MANY_TO_ONEتشير العديد من سجلات هذا الكائن إلى سجل واحد من الهدفنعم (joinColumnName)
ONE_TO_MANYيحتوي سجل واحد من هذا الكائن على العديد من سجلات الهدفلا (الجانب العكسي)

كيف تعمل العلاقات

تتطلّب كل علاقة حقلين يشيران إلى بعضهما البعض:
  1. جانب MANY_TO_ONE — يوجد على الكائن الذي يحمل المفتاح الخارجي.
  2. جانب ONE_TO_MANY — يوجد على الكائن الذي يملك المجموعة.
يستخدم كلا الحقلين FieldType.RELATION ويُحيل كلٌ منهما إلى الآخر عبر relationTargetFieldMetadataUniversalIdentifier.

مثال: البطاقة البريدية لديها العديد من المستلمين

يمكن إرسال PostCard إلى العديد من سجلات PostCardRecipient. ينتمي كل مستلم إلى بطاقة بريدية واحدة بالضبط. الخطوة 1: عرّف جانب ONE_TO_MANY على PostCard (جانب “الواحد”):
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: عرّف جانب MANY_TO_ONE على PostCardRecipient (جانب “العديد” — يحمل المفتاح الخارجي):
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 الخاص بالآخر. لتجنّب مشكلات الاستيراد الدائري، صدِّر معرّفات الحقول كثوابت مسمّاة من كل ملف، واستورِدها في الملف الآخر. يقوم نظام البناء بحلّها في وقت التجميع.

الربط مع الكائنات القياسية

لإنشاء علاقة مع كائن Twenty مضمّن (Person, Company, etc.)، استخدم 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',
  },
});

خصائص حقل العلاقة

الخاصيةمطلوبالوصف
typeنعميجب أن يكون FieldType.RELATION
relationTargetObjectMetadataUniversalIdentifierنعمقيمة universalIdentifier للكائن الهدف
relationTargetFieldMetadataUniversalIdentifierنعمقيمة universalIdentifier للحقل المطابق على الكائن الهدف
universalSettings.relationTypeنعمRelationType.MANY_TO_ONE أو RelationType.ONE_TO_MANY
universalSettings.onDeleteMANY_TO_ONE فقطماذا يحدث عند حذف السجل المشار إليه: CASCADE، SET_NULL، RESTRICT، أو NO_ACTION
universalSettings.joinColumnNameMANY_TO_ONE فقطاسم عمود قاعدة البيانات للمفتاح الخارجي (مثل postCardId)

حقول العلاقات المضمَّنة

يمكنك أيضًا إعلان علاقة مباشرة داخل defineObject. عند كونها مضمَّنة، احذِف objectUniversalIdentifier — إذ يُورَّث من الكائن الأب:
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
  ],
});