Presentation-Driven Generic Object Explorer
Status: Implemented, 2026-10-04 Scope: /objects/{id} ordinary-object path, presentation-driven rendering, no automatic relationship discovery
This document records the generic Object Explorer implementation slice. Its field-oriented renderer is one valid presentation composition, not a replacement for the global Object Presentation contract. The cross-cutting contract, binding semantics, authority boundary, renderable model and relationship to capability compositions are defined in Core Object Model and Authority; Views and Content owns the View/UI-specific mechanism.
Architectural Principle
Nothing becomes visible merely because it exists in the data graph. Every navigable relationship or object reference must be explicitly exposed by the presentation definition.
Design Pattern
/objects/{id}
↓
resolve object from repository
↓
resolve an optional Presentation Instance binding, then the applicable Business-scoped Presentation Definition
↓
project object using presentation.detail_fields
↓
render generic presentation
↓
Three rendering modes:
├── Primitive: Name: value
├── Object reference: <a href="/objects/{id}">Label</a>
└── Collection: Name / ID / View (multiple entries)
↓
HTML output
↓
When user clicks object reference: /objects/{id} (same process with new object's presentation)
No Automatic Traversal
The old system would:
- Query the database for all relationships
- Automatically display every relationship found
- Treat data existence as presentation intent
This violated the separation of:
- **Data graph**: relationships as they exist
- **Presentation**: which relationships are exposed in which UI context
New System
1. Presentation Definition Controls Visibility
- `detail_fields` array in presentation configuration specifies which fields appear
- No field → not shown, even if it exists in the data
- Object references must be explicitly listed in presentation
2. Object References as Explicit Fields
- A field with `renderer: 'object_reference'` becomes a clickable link
- Links point to canonical `/objects/{id}` endpoint
- The referenced object's own presentation determines what can be explored next
3. Same Object, Multiple Presentations
Data graph (one Campaign with 5 relationships):
Campaign ─┬─ Goal
├─ Audience
├─ Content
├─ Response
└─ Owner
Presentation A: Goal, Audience, Content
Presentation B: Goal, Audience, Response
Presentation C: Goal (summary only)
All three use the same underlying Campaign object; none creates a copied business Object.
Same URL (/objects/{id}) with different view_id query params
renders different interface.
Implementation Details
TargetHtmlBoundary::objectDetail()
Responsible for: 1. Resolving the object by ID 2. Resolving the optional Presentation Instance binding and Business-scoped Presentation Definition 3. Projecting the object using presentation.detail_fields 4. Passing the resulting projected field model to the thin renderer
Does NOT:
- Auto-discover relationships
- Query for additional fields
- Make assumptions about what should be shown
Shared `presentation.php` Template
Responsible for: 1. Rendering projected primitives as Name: value 2. Rendering object references as links to /objects/{id} 3. Rendering collections as lists of linked objects 4. Displaying state, type, and actions
Does NOT:
- Query the database for relationships
- Decide which fields to show (presentation decides that)
- Contain domain-specific rendering logic
GenericObjectsExplorer (JSON API)
Separate responsibility from HTML rendering:
- Provides JSON API for object traversal
- Currently used for generic exploration
- Should eventually align with presentation-driven semantics
How to Add a Visible Relationship
To expose a relationship in a presentation:
1. Add to presentation's detail_fields array:
{
"name": "related_organization",
"label": "Organization",
"renderer": "object_reference"
}
2. Ensure object_value contains the reference:
{
"related_organization": {
"kind": "object_reference",
"id": "org-uuid",
"revision": 1
}
}
3. The view now shows it as a link:
Organization: TechCorp Industries [link to /objects/org-uuid]
4. User clicks link:
- Navigates to `/objects/org-uuid`
- TechCorp's Presentation Configuration is resolved
- TechCorp's detail_fields determine what is shown next
- Recursion continues based on each object's presentation
Verification
- [x] `/objects/{id}` is canonical endpoint
- [x] `presentation.php` is the single generic rendering layer for object lists, details and revisions
- [x] Presentation definition controls visibility
- [x] Ordinary objects can render directly from a Business Presentation Definition without an empty instance object
- [x] No automatic relationship discovery
- [x] Object references link to `/objects/{id}`
- [x] Same object URL with different view_id shows different presentation
- [ ] Full end-to-end testing with navigation
- [ ] Update GenericObjectsExplorer to align with pattern
Related Architecture Documents
- [Views and Content](../architecture/views-and-content.md) - presentation semantics
- [Core Object Model and Authority](../architecture/core-object-model-and-authority.md) - object structure
- [Implementation Architecture](./architecture.md) - runtime mechanisms