You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix manifest missing component IRIs and Layout componentGroups embedding
Two bugs fixed:
1. Layout.componentGroups was returning embedded objects instead of IRI
strings. AP4 reads readableLink from getter methods, not property
declarations — override getComponentGroups() in Layout with
#[ApiProperty(readableLink: false, writableLink: false)] and add
explicit normalization/denormalization context with Layout:read/write
serialization groups.
2. resource_manifest was missing component IRIs from ComponentPositions
that use pageDataProperty. AP4 auto-computes readableLink=false for
ComponentPosition.component (AbstractComponent has no Route:manifest:read
fields), so AP4 returns the component as an IRI string rather than an
embedded object. ManifestDepthGroupTrait was only collecting @id values
from embedded arrays, not string IRIs.
Fix: collect top-level string IRIs in collectCurrentDepth, but skip
blank node resources (/.well-known/genid/) since AP4 assigns genid @ids
to plain objects like ResourceMetadata — their string properties are
internal metadata, not consumer-facing API resources.
PageDataNormalizer injects cwa_current_page_data into context when
serializing under Route:manifest:read so ComponentPositionNormalizer
can resolve pageDataProperty slots without an HTTP request.
### Bug: `Layout.componentGroups` returns embedded objects instead of IRIs
116
+
117
+
**Symptom (discovered 2026-06-16):** The navigation bar is empty for unauthenticated users. No failed requests appear in the network tab — the layout's component group contents are simply never fetched.
118
+
119
+
**Root cause:**`GET /_api/_/layouts/{uuid}` returns `componentGroups` as **full embedded objects**:
The module's `fetchAssociatedResources` expects all associated property values to be **IRI strings** (this is both the module's contract and the caching architecture principle: "Never embed related resource data — always return IRIs"). Receiving objects instead of strings causes a silent TypeError (`object.split is not a function`) that is swallowed by `fetchBatch`, so the component groups are never stored and `CwaComponentGroup` finds nothing.
125
+
126
+
**Why page content still works:** The manifest response includes page component group IRIs directly in `resource_iris`, so they are fetched as standalone resources in the manifest batch. Layouts have no manifest and rely entirely on `fetchAssociatedResources`.
127
+
128
+
**Required fix:** Change the `Layout` serialization group so `componentGroups` is serialized as an array of IRI strings only (not embedded objects). Check `ComponentGroup.componentPositions` for the same issue — the module also expects these to be IRI strings.
129
+
130
+
The module will be updated with a defensive `@id` extraction as a fallback, but the correct fix is here: the API must return IRIs, not embedded objects, for all associated resource properties.
131
+
115
132
### API endpoints
116
133
117
134
| Endpoint | Purpose |
@@ -196,6 +213,139 @@ Currently `parentPage` is only in `Route:manifest:read`. It needs to be added to
196
213
197
214
A Behat test should cover: `GET /_/pages` response includes `parentPage` for a page that has one set.
198
215
216
+
### Outstanding — UUID-based manifest must walk the `parentPage` chain
217
+
218
+
**Bug (discovered 2026-06-16):** When the Nuxt module admin accesses a nested `Page` entity directly via its admin URL (e.g. `/_cwa/%2F_api%2F_%2Fpages%2F{child-uuid}`), the fetcher calls `GET /_api/_/resource_manifest/{child-uuid}`. The module code is correct: it uses `irisByDepth[0]` as the parent depth and renders `pageIriAtDepth(depth)` for each level. However, the admin admin page displays only a placeholder (no parent content) because the manifest endpoint currently returns only the accessed page in a single depth group — it does not walk the `parentPage`/`parentPageData` chain upward.
219
+
220
+
**Required fix:**`ResourceManifestNormalizer` (or `ResourceManifestStateProvider`) when resolving by Page UUID must walk the `parentPage`/`parentPageData` chain to the root and produce `resource_iris: string[][]` with one inner array per depth level, root first — exactly as the route-path path does when the manifest normalizer walks the embedded parent sub-tree via the `Route:manifest:read` group.
221
+
222
+
For a chapter `Page` entity whose `parentPage` is a topic `Page`:
The fix should mirror what `RouteNormalizer` does when following `parentPage`/`parentPageData` during route-based manifest generation. The `ManifestDepthGroupTrait``buildDepthGroups` should already handle this if the correct sub-tree is passed in — check whether `ResourceManifestNormalizer` is passing the full serialized entity (including embedded parent data) or only the top-level page object.
233
+
234
+
A Behat test should cover: `GET /_/resource_manifest/{child-page-uuid}` for a page with `parentPage` set returns `resource_iris` with two depth groups (parent resources first, child resources last).
235
+
236
+
---
237
+
238
+
## Feature: CwaFixtureBuilder
239
+
240
+
> **Status: Design agreed, not yet implemented.**
241
+
242
+
A fluent builder API that lets developers scaffold CWA website structure (layouts, pages, component groups, components, routes) in Doctrine fixture code with minimal boilerplate. The Doctrine Fixtures Bundle handles execution; this feature adds the ergonomic PHP API on top.
243
+
244
+
### Dream developer API
245
+
246
+
```php
247
+
class AppScaffold extends AbstractCwaScaffold
248
+
{
249
+
public function build(CwaFixtureBuilder $cwa): void
0 commit comments