Relationship System Architecture
Overview
Section titled “Overview”This document describes the technical architecture of the bidirectional relationship system, including how it works under the hood and how to extend it.
Note: This document focuses specifically on the relationship sync system. For broader system documentation, see the Documentation Index at the end of this file.
System Components
Section titled “System Components”Backend Components
Section titled “Backend Components”InverseRelationships Class
Section titled “InverseRelationships Class”File: includes/class-inverse-relationships.php
The core class that handles automatic inverse relationship synchronization.
Key Methods:
sync_inverse_relationships($post_id): Main entry point called when a person is savedsync_single_inverse_relationship(): Creates or updates a single inverse relationshipremove_inverse_relationship(): Removes an inverse relationship when original is deletednormalize_relationships(): Handles different ACF return formats for comparisoncapture_old_relationships(): Captures old relationship values before ACF updates them
Hooks Used:
acf/update_value/name=relationships: Captures old values before updateacf/save_post: Triggers sync after relationships are saved
Data Flow:
User saves person → ACF saves relationships field →capture_old_relationships() stores old value →ACF updates field →sync_inverse_relationships() compares old vs new →sync_single_inverse_relationship() creates/updates inverses →update_field() saves inverse (triggers hook again, but protected by $processing)ACF Field Structure
Section titled “ACF Field Structure”Person Fields (acf-json/group_person_fields.json):
{ "name": "relationships", "type": "repeater", "sub_fields": [ { "name": "related_person", "type": "post_object", "return_format": "id" }, { "name": "relationship_type", "type": "taxonomy", "taxonomy": "relationship_type", "return_format": "id" }, { "name": "relationship_label", "type": "text" } ]}Relationship Type Fields (acf-json/group_relationship_type_fields.json):
{ "name": "inverse_relationship_type", "type": "taxonomy", "taxonomy": "relationship_type", "return_format": "id"}Frontend Components
Section titled “Frontend Components”RelationshipForm Component
Section titled “RelationshipForm Component”File: src/pages/People/RelationshipForm.jsx
- Form for creating/editing individual relationships
- Uses
SearchablePersonSelectorfor person selection - Fetches relationship types from REST API
- Updates person’s relationships array via REST API
RelationshipTypes Component
Section titled “RelationshipTypes Component”File: src/pages/Settings/RelationshipTypes.jsx
- Management interface for relationship types
- Create, edit, delete relationship types
- Configure inverse mappings using
SearchableRelationshipTypeSelector - Updates taxonomy terms via REST API
API Client
Section titled “API Client”File: src/api/client.js
Methods:
getRelationshipTypes(): Fetch all relationship typescreateRelationshipType(): Create new relationship typeupdateRelationshipType(): Update relationship type (including ACF fields)deleteRelationshipType(): Delete relationship type
REST API Integration
Section titled “REST API Integration”ACF Field Exposure
Section titled “ACF Field Exposure”File: includes/class-rest-api.php
The REST API exposes ACF fields for relationship types:
add_filter('rest_prepare_relationship_type', 'add_acf_to_relationship_type');add_action('rest_insert_relationship_type', 'update_relationship_type_acf');This allows the frontend to:
- Read inverse relationship type mappings
- Update inverse mappings via REST API
Data Flow Diagrams
Section titled “Data Flow Diagrams”Creating a Relationship
Section titled “Creating a Relationship”sequenceDiagram participant User participant Frontend participant REST API participant ACF participant InverseSync
User->>Frontend: Create relationship A→B Frontend->>REST API: POST /wp/v2/people/{id} REST API->>ACF: Save relationships field ACF->>InverseSync: Trigger acf/save_post hook InverseSync->>InverseSync: Get inverse mapping for type InverseSync->>ACF: Update person B's relationships ACF->>InverseSync: Trigger hook (protected by $processing) InverseSync->>InverseSync: Skip (already processing) ACF-->>REST API: Success REST API-->>Frontend: Updated person data Frontend-->>User: Relationship created (both sides)Updating a Relationship
Section titled “Updating a Relationship”sequenceDiagram participant User participant Frontend participant REST API participant ACF participant InverseSync
User->>Frontend: Change relationship type Frontend->>REST API: POST /wp/v2/people/{id} REST API->>ACF: Update relationships field ACF->>InverseSync: Capture old value ACF->>ACF: Save new value ACF->>InverseSync: Trigger sync InverseSync->>InverseSync: Compare old vs new InverseSync->>InverseSync: Remove old inverse InverseSync->>InverseSync: Create new inverse InverseSync->>ACF: Update person BExtension Points
Section titled “Extension Points”Adding Custom Relationship Logic
Section titled “Adding Custom Relationship Logic”To add custom logic when relationships are created:
// In your theme's functions.php or a custom pluginadd_action('rondo_relationship_created', function($from_person_id, $to_person_id, $relationship_type_id) { // Your custom logic here}, 10, 3);Filtering Inverse Mappings
Section titled “Filtering Inverse Mappings”To override inverse mappings programmatically:
add_filter('rondo_inverse_relationship_type', function($inverse_type_id, $relationship_type_id, $from_person_id, $to_person_id) { // Custom logic to determine inverse return $inverse_type_id;}, 10, 4);Custom Gender Resolution
Section titled “Custom Gender Resolution”To customize gender-dependent resolution:
add_filter('rondo_resolve_gender_dependent_type', function($resolved_type_id, $group, $gender, $related_person_id) { // Custom resolution logic return $resolved_type_id;}, 10, 4);Database Structure
Section titled “Database Structure”Relationships Storage
Section titled “Relationships Storage”Relationships are stored in WordPress post meta:
- Meta Key:
relationships(ACF field) - Meta Value: Serialized array of relationship objects
- Post Type:
person
Each relationship entry:
[ 'related_person' => 123, // Person post ID 'relationship_type' => 45, // Taxonomy term ID 'relationship_label' => '', // Optional custom label]Inverse Mappings Storage
Section titled “Inverse Mappings Storage”Inverse mappings are stored in taxonomy term meta:
- Taxonomy:
relationship_type - Meta Key:
inverse_relationship_type(ACF field) - Meta Value: Taxonomy term ID of the inverse type
Performance Considerations
Section titled “Performance Considerations”Caching
Section titled “Caching”- Relationship types are cached by TanStack Query on the frontend
- WordPress caches taxonomy terms internally
- ACF fields are cached by WordPress object cache (if enabled)
Optimization
Section titled “Optimization”- The
$processingarray prevents infinite loops - Old relationship values are captured once per save operation
- Normalization happens in memory before comparison
Scalability
Section titled “Scalability”- The system processes relationships one person at a time
- For bulk imports, consider batching person updates
- Large numbers of relationships per person may impact performance
Security
Section titled “Security”Access Control
Section titled “Access Control”- Relationship creation respects
Rondo\Core\AccessControlpermissions - Users can only create relationships for people they can access
- Inverse relationships inherit the same access restrictions
Data Validation
Section titled “Data Validation”- Person IDs are validated before creating inverses
- Relationship type IDs are validated against taxonomy
- ACF field values are sanitized by ACF Pro
Testing
Section titled “Testing”Unit Testing Considerations
Section titled “Unit Testing Considerations”When testing the relationship system:
- Test inverse creation: Verify correct inverse is created
- Test inverse updates: Verify inverse updates when original changes
- Test inverse deletion: Verify inverse is removed when original is deleted
- Test edge cases: Missing people, missing types, circular references
- Test gender resolution: Verify gender-dependent types resolve correctly
Manual Testing Checklist
Section titled “Manual Testing Checklist”- Create relationship → verify inverse created
- Edit relationship type → verify inverse updated
- Delete relationship → verify inverse deleted
- Create symmetric relationship → verify same type on both sides
- Create gender-dependent relationship → verify correct type resolved
- Test with missing gender → verify fallback behavior
- Test with missing inverse mapping → verify no inverse created
Future Enhancements
Section titled “Future Enhancements”Potential improvements to the system:
- Gender-dependent groups: Support for grouping gender variants (aunt/uncle, niece/nephew)
- Bulk operations: Tools for bulk updating relationship types
- Relationship validation: Prevent invalid relationship combinations
- Relationship history: Track when relationships were created/modified
- Import/export: Tools for exporting relationship data
Related Documentation
Section titled “Related Documentation”Core System Documentation
Section titled “Core System Documentation”| Document | Description |
|---|---|
| Data Model | Post types, taxonomies, and ACF field definitions |
| Access Control | Row-level security and user permissions |
| REST API | All API endpoints (WordPress + custom) |
| PHP Autoloading | Conditional class loading system |
Feature Documentation
Section titled “Feature Documentation”| Document | Description |
|---|---|
| Relationships | How bidirectional relationships work |
| Relationship Types | Configuring relationship type mappings |
| Import | Contact import from vCard and Google |
| iCal Feed | Calendar subscription system |
| Reminders | Email notification system |
Frontend Documentation
Section titled “Frontend Documentation”| Document | Description |
|---|---|
| Frontend Architecture | React SPA structure and patterns |
Project Documentation
Section titled “Project Documentation”| Document | Description |
|---|---|
| README | Project overview and quick start |
| AGENTS.md | AI agent guidance and development rules |
| CHANGELOG | Version history |