Turfi Platform Documentation
Official Turfi documentation portal for users, admins, and developers.
Documentation Search
Search only within Turfi documentation pages.
C
Contacts
Shared contact registry contract.
Entity: Contacts
1. Overview
- Shared normalized contact registry for reusable person/contact records linked across entities and facilities.
- Acts as a reusable shared-data entity.
- Can be linked through centralized entity-contact relationships and facility forms.
- Visible at
/admin/data/registries/contacts.
2. Database Schema
Columns
| column | type | nullable | default | notes | |
|---|---|---|---|---|---|
| created_at | string | no | database default or generated | Base table field. | |
| string \ | null | yes | null | Base table field. | |
| first_name | string \ | null | yes | null | Base table field. |
| full_name | string \ | null | yes | null | Base table field. |
| id | string | no | generated UUID default | Base table field. | |
| last_name | string \ | null | yes | null | Base table field. |
| normalized_name | string \ | null | yes | null | Base table field. |
| notes | string \ | null | yes | null | Base table field. |
| phone | string \ | null | yes | null | Base table field. |
| search_name | string \ | null | yes | null | Base table field. |
| status | string | no | database default or generated | Lifecycle field when present. | |
| title | string \ | null | yes | null | Base table field. |
| updated_at | string | no | database default or generated | Base table field. | |
| website | string \ | null | yes | null | Base table field. |
Foreign Keys
No foreign keys documented by the generated schema contract.
Indexes
No migration-defined indexes were discovered in the checked-in SQL history for this table.
Constraints
- Unique: none discovered in checked-in migrations.
- Check: none discovered in checked-in migrations.
Triggers
No migration-defined triggers were discovered in the checked-in SQL history for this table.
Views (if any)
| name | purpose |
|---|---|
| api_contacts | Shared-data read model for contact registry operations. |
3. Import Contract
Accepted Columns
keyslugfull_namefirst_namelast_namelabelcontact_nameemailphonewebsitenotesstatus
Resolution Rules
- Direct contact imports normalize names from
first_name,last_name,full_name,contact_name, or fallbackname. - Contact matching checks direct
idfirst, then exact combinations offull_name,email, andphone. - If exactly one existing contact matches, the importer updates that contact; if none match, it creates a new one; if multiple match, the row fails as ambiguous.
Required Fields
first_name
Optional Fields
keyslugfull_namelast_namelabelcontact_nameemailphonewebsitenotesstatus
Failure Rules
- Missing
first_namefails immediately. - Invalid status or malformed contact fields fail validation rather than relationship mismatch handling.
4. Frontend Registry Mapping
Grid Columns
| label | field | source |
|---|---|---|
| First Name | first_name | derived structured name |
| Last Name | last_name | derived structured name |
| Title | title | contacts.label |
| normalized contacts.email | ||
| Phone | phone | normalized contacts.phone |
| Status | status | contacts.status |
Filters
| filter | field | type |
|---|---|---|
| Search | full_name, first_name, last_name, title, email, phone | text substring |
| First Name | first_name | text |
| Last Name | last_name | text |
| text | ||
| Title | label | text |
| Status | status | select |
Display Logic
- Structured names are derived from first/last name where available, otherwise split from full-name style fields.
- The displayed
Titlecolumn is backed by thelabelfield. - Phone values are normalized and formatted in-grid.
5. Lifecycle Rules
- Shared contacts should be inactivated or relinked rather than hard-deleted when downstream entities still reference them.
- Contact lifecycle should preserve communication history and relationship summaries.
Relationships
- There is no direct
contact_idcolumn on the core registry tables documented here. entity_contacts.contact_id -> contacts.idis the reusable linking table for shared contacts.entity_contacts.contact_role_id -> contact_roles.idassigns an optional semantic role to each link.entity_contactssupportsclub,organization,venue,business, anduserentity types.- The model is many-to-many: one contact can be linked to many entities, and one entity can have many contacts.
- Primary-link behavior is enforced by
idx_entity_contacts_primary_uniqueplustrg_entity_contacts_single_primary. - When the final
entity_contactslink is removed,trg_entity_contacts_cleanup_orphandeletes the orphaned contact record automatically.
Import Contract Detail
- Contacts are imported as first-class shared records through the
contactsimport adapter. - Current organization/league/club import adapters do not create inline shared contact rows during CSV import the way they do for inline addresses.
- Inline contact creation and linking is supported in admin entity editors that save reusable contact bundles through
saveEntityContactAddressBundle(). - Required import field is
first_name; the adapter also acceptsfull_name,last_name,label,contact_name,email,phone,website,notes, andstatus. - When an existing contact is found by exact
full_name/email/phonematching, the record is updated instead of duplicated.
Views / Joins
api_contactsreads directly fromcontactsand addslinked_entity_countplusprimary_entity_countfromentity_contacts.- The top-level contacts registry shows one row per contact, not one row per entity-contact link.
- Structured display names are derived in the UI from
first_nameandlast_name, with fallback tofull_nameorcontact_name. - When bundle-linked contacts are loaded for an entity, the service orders primary contacts first and then additional contacts by
display_orderascending. - Current organization, league, and club grids do not render multi-contact lists inline; contact editing there is either direct summary-field editing or handled in dedicated bundle editors for entities that support them.
Usage Across Entities
- Current shared-link consumers in
entity_contacts: clubs, organizations, venues, businesses, and users. - Summary phone propagation is implemented for organizations, businesses, and users through
trg_contacts_refresh_dependentsandrefresh_entity_primary_contact_summary(). - Organizations, leagues, and clubs also store direct summary fields such as
phone,email, andwebsite, but those are not modeled as direct contact FKs. - Because contacts are shared, updating one contact row affects every entity linked to that same contact record.
- Display priority for linked contacts is: primary contact first, then additional contacts ordered by
display_order.