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
213 lines
11 KiB
Plaintext
213 lines
11 KiB
Plaintext
---
|
|
title: Cílení na systémová metadata
|
|
description: Vyřešte deterministické univerzální identifikátory metadat, která Twenty automaticky zřizuje u každého objektu, aby se na ně vaše aplikace mohla odkazovat bez hardcodování.
|
|
icon: gears
|
|
---
|
|
|
|
Každý objekt v Twenty má **systémová metadata**, která sami nedefinujete, například sadu polí a hlavní zobrazení seznamu s jeho sloupci. Server všechno vytvoří při zřízení objektu a tato sada roste spolu s tím, jak roste Twenty.
|
|
|
|
Protože je nedefinujete, neexistuje žádná konstanta `universalIdentifier`, kterou byste mohli importovat. Místo toho server každý identifikátor **odvozuje** deterministicky a `twenty-sdk` zpřístupňuje stejné odvozování, takže váš manifest může zjistit přesnou hodnotu, kterou server používá.
|
|
|
|
## Systémová pole
|
|
|
|
Skalární pole přítomná na každém objektu, z nichž žádné nedefinujete pomocí [`defineField()`](/l/cs/developers/extend/apps/data/extending-objects):
|
|
|
|
`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`
|
|
|
|
Jak tedy odkážete na `createdAt` jako na sloupec ve [zobrazení](/l/cs/developers/extend/apps/layout/views)?
|
|
|
|
### Problém
|
|
|
|
Od Twenty 2.19 je univerzální identifikátor systémového pole **deterministicky odvozován** serverem ze tří vstupů: univerzálního identifikátoru aplikace, univerzálního identifikátoru objektu a názvu pole. Vymyslet si id a natvrdo ho zapsat nebude fungovat: neshoduje se s ničím na serveru a synchronizace zamítne osiřelý odkaz:
|
|
|
|
```
|
|
Dev sync failed: viewField: INVALID_VIEW_DATA: Field metadata not found
|
|
```
|
|
|
|
### Řešení
|
|
|
|
<Note>
|
|
`getFieldUniversalIdentifier` je k dispozici od `twenty-sdk` verze 2.21 a vyšší.
|
|
</Note>
|
|
|
|
Použijte `getFieldUniversalIdentifier` k odvození přesně stejné hodnoty, jakou používá server. Funkce přijímá tři vstupy a vrací univerzální identifikátor pole:
|
|
|
|
```ts
|
|
import { getFieldUniversalIdentifier } from 'twenty-sdk/define';
|
|
|
|
const createdAtFieldId = getFieldUniversalIdentifier({
|
|
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
|
objectUniversalIdentifier: MY_OBJECT_UNIVERSAL_IDENTIFIER,
|
|
name: 'createdAt',
|
|
});
|
|
```
|
|
|
|
* `applicationUniversalIdentifier` je identifikátor vaší aplikace, ten, který předáváte do [`defineApplication()`](/l/cs/developers/extend/apps/config/application).
|
|
* `objectUniversalIdentifier` je identifikátor objektu, ke kterému pole patří.
|
|
* `name` je název systémového pole, jedna z výše uvedených hodnot.
|
|
|
|
### Příklad: sloupec createdAt v zobrazení
|
|
|
|
Typickým případem je přidání sloupce `createdAt` do zobrazení některého z vašich vlastních objektů. Odvoďte identifikátor pole a odkažte na něj stejně jako na jakýkoli jiný `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,
|
|
},
|
|
],
|
|
});
|
|
```
|
|
|
|
Tentýž odvozený identifikátor funguje všude tam, kde se očekává `fieldMetadataUniversalIdentifier`: pole zobrazení, filtry, řazení, seskupování a widgety rozložení stránky.
|
|
|
|
<Note>
|
|
Identifikátor odvozujte, neukládejte ho natvrdo. Protože server odvozuje hodnotu z
|
|
ID aplikace, ID objektu a názvu pole, volání
|
|
`getFieldUniversalIdentifier` udržuje odkaz správný i v případě, že se tyto
|
|
vstupy změní, a předchází odchylkám, pokud se samotný způsob odvozování někdy změní.
|
|
</Note>
|
|
|
|
### Systémová relační pole
|
|
|
|
<Note>
|
|
`getSystemRelationFieldUniversalIdentifier` je k dispozici od verze `twenty-sdk`
|
|
2.23 a vyžaduje server Twenty ve verzi 2.23 nebo novější.
|
|
</Note>
|
|
|
|
Kromě výše uvedených skalárních systémových polí server také na každém objektu poskytuje čtyři **systémová relační pole**: `timelineActivities`, `attachments`, `noteTargets` a `taskTargets`, z nichž každé ukazuje na odpovídající standardní relační objekt.
|
|
|
|
Tato pole nejsou řešena pomocí `getFieldUniversalIdentifier`: jejich identifikátor je odvozen **bez ohledu na název** z objektu, který pole obsahuje, a z objektu, na který pole odkazuje. Tím pádem přejmenování objektu nikdy nezmění identifikátory jeho relačních polí.
|
|
|
|
Použijte `getSystemRelationFieldUniversalIdentifier` k jejich vyřešení:
|
|
|
|
```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` je objekt, který toto pole **obsahuje**.
|
|
* `relationTargetObjectUniversalIdentifier` je objekt, na který toto pole **ukazuje**.
|
|
|
|
Směr je zakódovaný pořadím argumentů. Pro vyřešení opačné strany (např. `attachment.targetRocket`, morph pole, které server vytvoří na standardním relačním objektu), je prohoďte:
|
|
|
|
```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,
|
|
});
|
|
```
|
|
|
|
Stejně jako u skalárních systémových polí funguje vyřešené ID všude tam, kde je očekáváno `fieldMetadataUniversalIdentifier`.
|
|
|
|
## Systémová zobrazení
|
|
|
|
<Note>
|
|
`getSystemViewUniversalIdentifier` a `getSystemViewFieldUniversalIdentifier`
|
|
jsou k dispozici od verze `twenty-sdk` 2.26 a vyžadují server Twenty ve
|
|
verzi 2.26 nebo novější.
|
|
</Note>
|
|
|
|
Server také pro každý objekt zřizuje **systémové zobrazení**: hlavní zobrazení seznamu (`All {objectLabelPlural}`, s klíčem `ViewKey.INDEX` (odvozeným pomocí `SYSTEM_VIEW_KEYS.INDEX`)), s jedním sloupcem pro každé zobrazitelné pole. Stejně jako u systémových relačních polí jsou jejich identifikátory odvozovány **nezávisle na názvu**, takže přejmenování objektu nebo pole je nikdy nezmění.
|
|
|
|
Použijte `getSystemViewUniversalIdentifier` k vyřešení zobrazení:
|
|
|
|
```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` je aplikace, která vlastní **objekt**, podle které je zobrazení namespacované.
|
|
* `objectUniversalIdentifier` je objekt, který zobrazení vypisuje.
|
|
* `viewKey` je diskriminátor systémového zobrazení: `SYSTEM_VIEW_KEYS.INDEX` pro hlavní zobrazení seznamu, `SYSTEM_VIEW_KEYS.FIELDS_WIDGET` pro zobrazení widgetu polí na stránce záznamu. Určuje klíč pro odvozování; pouze `INDEX` je také uložen v řádku zobrazení.
|
|
|
|
Vyhodnocené ID funguje všude tam, kde se očekává `viewUniversalIdentifier`, například u položky postranního panelu [`NavigationMenuItemType.VIEW`](/l/cs/developers/extend/apps/layout/navigation-menu-items). Pro pouhé otevření hlavního seznamu objektu preferujte `NavigationMenuItemType.OBJECT` s `targetObjectUniversalIdentifier`: nevyžaduje žádné odvozování.
|
|
|
|
`getSystemViewFieldUniversalIdentifier` vyhodnotí jeden konkrétní **sloupec** v systémovém zobrazení na základě zobrazení a pole, které zobrazuje:
|
|
|
|
```ts
|
|
import { getSystemViewFieldUniversalIdentifier } from 'twenty-sdk/define';
|
|
|
|
const rocketNameColumnId = getSystemViewFieldUniversalIdentifier({
|
|
fieldMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
|
viewUniversalIdentifier: rocketIndexViewId,
|
|
fieldMetadataUniversalIdentifier: ROCKET_NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
|
});
|
|
```
|
|
|
|
Všimněte si prvního argumentu: sloupec je namespacován podle aplikace, která vlastní **pole, které zobrazuje**, nikoli podle aplikace, která vlastní zobrazení. Pole, které vaše aplikace přidá ke standardnímu objektu, má svůj sloupec odvozený pod vaší aplikací, a to v rámci zobrazení vlastněného Twenty.
|
|
|
|
<Warning>
|
|
Systémová zobrazení a jejich sloupce jsou **vlastněna serverem**: vyhodnocujte jejich identifikátory pro účely odkazování, nikdy pro jejich deklaraci. `key` u
|
|
[`defineView()`](/l/cs/developers/extend/apps/layout/views) je zastaralý a
|
|
ignorovaný, takže manifestové zobrazení nikdy nemůže nárokovat klíč `INDEX` a server už zřizuje sloupec pro každé pole, které přidáte, takže deklarovat vlastní
|
|
`defineViewField()` pro stejné pole v systémovém zobrazení s tím bude v konfliktu.
|
|
</Warning>
|
|
|
|
## Standardní objekty Twenty
|
|
|
|
U **standardního** objektu Twenty (Person, Company, Opportunity, …) nemusíte nic odvozovat: identifikátory jsou předpočítané konstanty, které můžete přímo importovat, a to jak pro pole, tak pro zobrazení.
|
|
|
|
```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
|
|
```
|
|
|
|
Použijte pomocné funkce uvedené výše v případě, kdy je objekt definován **vaší aplikací** pomocí [`defineObject()`](/l/cs/developers/extend/apps/data/objects) a žádná taková konstanta neexistuje.
|
|
|
|
<Note>
|
|
`name` je **výchozí** pole, nikoli systémové pole. Má svůj vlastní natvrdo daný
|
|
univerzální identifikátor a neodvozuje se pomocí
|
|
`getFieldUniversalIdentifier`. U objektů, které definujete, odkazujte na pole
|
|
`name` pomocí identifikátoru, který jste mu dali v `defineObject()`.
|
|
</Note>
|