-
Notifications
You must be signed in to change notification settings - Fork 353
Description
Description
When previewing a page that contains a snippet_selection property, the preview fails with a ContentNotFoundException. This happens because ContentResolver re-aggregates snippets using the parent's stage (draft in preview mode), but snippets only have live dimension content available.
Sulu Version
- Sulu 3.0.1
- Symfony 7.2
Steps to Reproduce
- Create a snippet template (e.g.,
beer.xml) - Create a page template with a
snippet_selectionproperty:
<property name="beers" type="snippet_selection">
<params>
<param name="types" value="beer"/>
</params>
</property>- Create and publish a snippet
- Create a page, select the published snippet in the
snippet_selectionfield - Observe the preview panel - it shows an error
Expected Behavior
The preview should display the page with the resolved snippet content.
Actual Behavior
Preview fails with:
ContentNotFoundException: Could not load content with id "019b6573-9aa4-7188-899a-f0d2c31084f7" and attributes: {"locale":"en","stage":"draft"}
Root Cause Analysis
The issue is in ContentResolver.php at lines 75-80:
if ($resource instanceof ContentRichEntityInterface) {
$childContent = $this->contentAggregator->aggregate($resource, [
'locale' => $locale,
'stage' => $stage, // BUG: Uses parent's stage (draft in preview)
]);Flow:
SnippetResourceLoadercorrectly loads snippets withSTAGE_LIVE- It returns
Snippetentities (which implementContentRichEntityInterface) ContentResolversees these areContentRichEntityInterfaceinstances- It re-aggregates them using the parent's stage (
draftin preview mode) ContentAggregatorcan't find dimension content withstage=draftfor the snippetContentNotFoundExceptionis thrown
Why snippets don't have draft content:
Snippets are typically only published (live), not kept in draft state like pages being edited. When a user selects a snippet in a page, they expect to see the published version.
Suggested Fix
Snippets should always be resolved with STAGE_LIVE, regardless of the parent's stage. Possible solutions:
Option 1: Check resource type in ContentResolver
if ($resource instanceof ContentRichEntityInterface) {
$childStage = $resource instanceof SnippetInterface
? DimensionContentInterface::STAGE_LIVE
: $stage;
$childContent = $this->contentAggregator->aggregate($resource, [
'locale' => $locale,
'stage' => $childStage,
]);Option 2: Have SnippetResourceLoader return resolved DimensionContent
Instead of returning the Snippet entity, return the already-aggregated DimensionContent with STAGE_LIVE. This bypasses the ContentRichEntityInterface check.
Workaround
We implemented a custom SnippetResourceLoader that fully resolves snippets with STAGE_LIVE and returns the resolved data structure, bypassing the re-aggregation:
class PreviewSafeSnippetResourceLoader implements ResourceLoaderInterface
{
public function load(array $ids, ?string $locale, array $params = []): array
{
// Load snippets with STAGE_LIVE
$snippets = $this->snippetRepository->findBy([
'uuids' => $ids,
'locale' => $locale,
'stage' => DimensionContentInterface::STAGE_LIVE,
], [], [SnippetRepositoryInterface::GROUP_SELECT_SNIPPET_WEBSITE => true]);
$contentResolver = $this->container->get('sulu_content.content_resolver');
$mappedResult = [];
foreach ($snippets as $snippet) {
$dimensionContent = $this->contentAggregator->aggregate($snippet, [
'locale' => $locale,
'stage' => DimensionContentInterface::STAGE_LIVE,
]);
// Return fully resolved data instead of entity
$mappedResult[$snippet->getId()] = $contentResolver->resolve($dimensionContent);
}
return $mappedResult;
}
}Related
This may be related to similar issues with referenced content in preview mode.