Skip to content

Commit aeaa820

Browse files
committed
FEATURE: support for different output formats, with differing documentRenderers and custom enumerators
1 parent 12427ab commit aeaa820

11 files changed

Lines changed: 298 additions & 58 deletions

File tree

Classes/NodeEnumeration/Domain/Dto/EnumeratedNode.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,24 @@ final class EnumeratedNode implements \JsonSerializable
4141
*/
4242
protected $nodeTypeName;
4343

44-
private function __construct(string $contextPath, string $nodeIdentifier, string $nodeTypeName, array $arguments)
44+
/**
45+
* The renderer implementation to use for this EnumeratedNode;
46+
* a key from Settings at Flowpack.DecoupledContentStore.extensions.documentRenderers.[key]
47+
*/
48+
public readonly string $rendererId;
49+
50+
private function __construct(string $contextPath, string $nodeIdentifier, string $nodeTypeName, array $arguments, string $rendererId = '')
4551
{
4652
$this->contextPath = $contextPath;
4753
$this->nodeIdentifier = $nodeIdentifier;
4854
$this->nodeTypeName = $nodeTypeName;
4955
$this->arguments = $arguments;
56+
$this->rendererId = $rendererId;
5057
}
5158

5259
static public function fromNode(NodeInterface $node, array $arguments = []): self
5360
{
54-
return new self($node->getContextPath(), $node->getIdentifier(), $node->getNodeType()->getName(), $arguments);
61+
return new self($node->getContextPath(), $node->getIdentifier(), $node->getNodeType()->getName(), $arguments, '');
5562
}
5663

5764
static public function fromJsonString(string $enumeratedNodeString): self
@@ -60,7 +67,7 @@ static public function fromJsonString(string $enumeratedNodeString): self
6067
if (!is_array($tmp)) {
6168
throw new \Exception('EnumeratedNode cannot be constructed from: ' . $enumeratedNodeString);
6269
}
63-
return new self($tmp['contextPath'], $tmp['nodeIdentifier'], $tmp['nodeTypeName'] ?? '', $tmp['arguments']);
70+
return new self($tmp['contextPath'], $tmp['nodeIdentifier'], $tmp['nodeTypeName'] ?? '', $tmp['arguments'], $tmp['rendererId']);
6471
}
6572

6673
public function jsonSerialize()
@@ -69,7 +76,8 @@ public function jsonSerialize()
6976
'contextPath' => $this->contextPath,
7077
'nodeIdentifier' => $this->nodeIdentifier,
7178
'nodeTypeName' => $this->nodeTypeName,
72-
'arguments' => $this->arguments
79+
'arguments' => $this->arguments,
80+
'rendererId' => $this->rendererId,
7381
];
7482
}
7583

@@ -105,6 +113,11 @@ public function getArguments(): array
105113

106114
public function debugString(): string
107115
{
108-
return sprintf('%s %s %s(%s)', $this->nodeTypeName, $this->nodeIdentifier, $this->arguments ? http_build_query($this->arguments) . ' ' : '', $this->contextPath);
116+
return sprintf('%s %s %s(%s) - %s', $this->nodeTypeName, $this->nodeIdentifier, $this->arguments ? http_build_query($this->arguments) . ' ' : '', $this->contextPath, $this->rendererId);
117+
}
118+
119+
public function withRendererId(string $rendererId): self
120+
{
121+
return new self($this->contextPath, $this->nodeIdentifier, $this->nodeTypeName, $this->arguments, $rendererId);
109122
}
110123
}

Classes/NodeEnumeration/NodeEnumerator.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Repository\RedisEnumerationRepository;
1212
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Service\NodeContextCombinator;
1313
use Flowpack\DecoupledContentStore\NodeRendering\Dto\NodeRenderingCompletionStatus;
14+
use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\NodeRenderingExtensionManager;
1415
use Flowpack\DecoupledContentStore\PrepareContentRelease\Infrastructure\RedisContentReleaseService;
1516
use Flowpack\DecoupledContentStore\Utility\GeneratorUtility;
1617
use Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory;
@@ -44,6 +45,12 @@ class NodeEnumerator
4445
*/
4546
protected $concurrentBuildLockService;
4647

48+
/**
49+
* @Flow\Inject
50+
* @var NodeRenderingExtensionManager
51+
*/
52+
protected $nodeRenderingExtensionManager;
53+
4754
/**
4855
* @Flow\InjectConfiguration("nodeRendering.nodeTypeWhitelist")
4956
* @var string
@@ -63,9 +70,10 @@ public function enumerateAndStoreInRedis(?Site $site, ContentReleaseLogger $cont
6370
foreach (GeneratorUtility::createArrayBatch($this->enumerateAll($site, $contentReleaseLogger, $newMetadata->getWorkspaceName()), 100) as $enumeration) {
6471
$this->concurrentBuildLockService->assertNoOtherContentReleaseWasStarted($releaseIdentifier);
6572
// $enumeration is an array of EnumeratedNode, with at most 100 elements in it.
66-
// TODO: EXTENSION POINT HERE, TO ADD ADDITIONAL ENUMERATIONS (.metadata.json f.e.)
67-
// TODO: not yet fully sure how to handle Enumeration
73+
6874
$this->redisEnumerationRepository->addDocumentNodesToEnumeration($releaseIdentifier, ...$enumeration);
75+
76+
// DEPRECATED: use extensions.documentRenderers.[...].enumeratorClassName instead
6977
foreach ($enumeration as $enumeratedNode) {
7078
$this->emitNodeEnumerated($enumeratedNode, $releaseIdentifier, $contentReleaseLogger);
7179
}
@@ -81,7 +89,7 @@ private function enumerateAll(?Site $site, ContentReleaseLogger $contentReleaseL
8189

8290
$nodeTypeWhitelist = $this->nodeTypeConstraintFactory->parseFilterString($this->nodeTypeWhitelist);
8391

84-
$queueSite = function (Site $site) use ($combinator, &$documentNodeVariantsToRender, $nodeTypeWhitelist, $contentReleaseLogger, $workspaceName) {
92+
$queueSite = function (Site $site) use ($combinator, $nodeTypeWhitelist, $contentReleaseLogger, $workspaceName) {
8593
$contentReleaseLogger->debug('Publishing site', [
8694
'name' => $site->getName(),
8795
'domain' => $site->getFirstActiveDomain()
@@ -97,12 +105,15 @@ private function enumerateAll(?Site $site, ContentReleaseLogger $contentReleaseL
97105
$contextPath = $documentNode->getContextPath();
98106

99107
if ($nodeTypeWhitelist->matches(NodeTypeName::fromString($documentNode->getNodeType()->getName()))) {
100-
101-
$contentReleaseLogger->debug('Registering node for publishing', [
102-
'node' => $contextPath
103-
]);
104-
105-
yield EnumeratedNode::fromNode($documentNode);
108+
foreach ($this->nodeRenderingExtensionManager->enumerateDocumentNode($documentNode) as $enumeratedNode) {
109+
$contentReleaseLogger->debug('Registering node for publishing', [
110+
'node' => $contextPath,
111+
'renderer' => $enumeratedNode->rendererId,
112+
'arguments' => $enumeratedNode->getArguments(),
113+
]);
114+
115+
yield $enumeratedNode;
116+
}
106117
} else {
107118
$contentReleaseLogger->debug('Skipping node from publishing, because it did not match the configured nodeTypeWhitelist', [
108119
'node' => $contextPath,
@@ -127,6 +138,8 @@ private function enumerateAll(?Site $site, ContentReleaseLogger $contentReleaseL
127138
*
128139
* This signal can be used to add additional EnumeratedNode entries (e.g. with added arguments for pagination or filters) based on the given node.
129140
*
141+
* DEPRECATED: use extensions.documentRenderers.[...].enumeratorClassName instead
142+
*
130143
* @param EnumeratedNode $enumeratedNode
131144
* @param ContentReleaseIdentifier $releaseIdentifier
132145
* @param ContentReleaseLogger $contentReleaseLogger
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Flowpack\DecoupledContentStore\NodeRendering\Extensibility;
5+
6+
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Dto\EnumeratedNode;
7+
use Neos\ContentRepository\Domain\Model\NodeInterface;
8+
9+
/**
10+
* Adjust document enumeration.
11+
*
12+
* The Document Enumeration decides for every Document Node what variants should appear
13+
* in the resulting Content Release.
14+
*
15+
* It should return an array of {@see EnumeratedNode} objects.
16+
*
17+
* This can be used to:
18+
* - render document nodes in different variants, e.g. formats
19+
* - use different renderers (configured via Flowpack.DecoupledContentStore.extensions.documentRenderers.[...])
20+
* - decide at runtime which (document) nodes should be part of a content release and which not.
21+
*/
22+
interface DocumentEnumeratorInterface
23+
{
24+
public function __construct(array $options = []);
25+
26+
/**
27+
* @return iterable<EnumeratedNode>
28+
*/
29+
public function enumerateDocumentNode(NodeInterface $documentNode): iterable;
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentEnumerators;
4+
5+
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Dto\EnumeratedNode;
6+
use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentEnumeratorInterface;
7+
use Neos\ContentRepository\Domain\Model\NodeInterface;
8+
9+
class DefaultEnumerator implements DocumentEnumeratorInterface
10+
{
11+
public function __construct(array $options = [])
12+
{
13+
}
14+
15+
public function enumerateDocumentNode(NodeInterface $documentNode): iterable
16+
{
17+
return [
18+
EnumeratedNode::fromNode($documentNode),
19+
];
20+
}
21+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentEnumerators;
4+
5+
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Dto\EnumeratedNode;
6+
use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentEnumeratorInterface;
7+
use Neos\ContentRepository\Domain\Model\NodeInterface;
8+
9+
/**
10+
* An enumerator which only renders the first N pages. very useful for interactive testing.
11+
* NOT useful for production.
12+
*/
13+
class LimitEnumerator implements DocumentEnumeratorInterface
14+
{
15+
protected int $i = 0;
16+
private int $limit;
17+
18+
public function __construct(
19+
array $options = []
20+
) {
21+
$this->limit = $options['limit'] ?? throw new \InvalidArgumentException('Missing limit option');
22+
}
23+
public function enumerateDocumentNode(NodeInterface $documentNode): iterable
24+
{
25+
if ($this->i++ >= $this->limit) {
26+
return [];
27+
}
28+
29+
return [
30+
EnumeratedNode::fromNode($documentNode),
31+
];
32+
}
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Flowpack\DecoupledContentStore\NodeRendering\Extensibility;
5+
6+
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
7+
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Dto\EnumeratedNode;
8+
use Flowpack\DecoupledContentStore\NodeRendering\Dto\RenderedDocumentFromContentCache;
9+
use Flowpack\DecoupledContentStore\NodeRendering\NodeRenderer;
10+
use Flowpack\DecoupledContentStore\NodeRendering\NodeRenderOrchestrator;
11+
use Neos\ContentRepository\Domain\Model\NodeInterface;
12+
13+
/**
14+
* Decide how to render a document node. Used throughout {@see NodeRenderOrchestrator} and {@see NodeRenderer}
15+
* for the specific rendering logic.
16+
*
17+
* If you want to create additional output formats (e.g. JSON), or you want rendering without
18+
* Fusion, you'll need to create a custom DocumentRenderer.
19+
*/
20+
interface DocumentRendererInterface
21+
{
22+
public function tryToExtractRenderingForEnumeratedNodeFromContentCache(EnumeratedNode $enumeratedNode): RenderedDocumentFromContentCache;
23+
24+
public function renderDocumentNodeVariant(NodeInterface $node, EnumeratedNode $enumeratedNode, ContentReleaseLogger $contentReleaseLogger);
25+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentRenderers;
4+
5+
use Neos\Flow\Annotations as Flow;
6+
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
7+
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Dto\EnumeratedNode;
8+
use Flowpack\DecoupledContentStore\NodeRendering\Dto\DocumentNodeCacheKey;
9+
use Flowpack\DecoupledContentStore\NodeRendering\Dto\RenderedDocumentFromContentCache;
10+
use Flowpack\DecoupledContentStore\NodeRendering\Extensibility\DocumentRendererInterface;
11+
use Flowpack\DecoupledContentStore\NodeRendering\Infrastructure\RedisContentCacheReader;
12+
use Flowpack\DecoupledContentStore\NodeRendering\Render\DocumentRenderer;
13+
use Neos\ContentRepository\Domain\Model\NodeInterface;
14+
15+
class FusionHtmlRenderer implements DocumentRendererInterface
16+
{
17+
18+
/**
19+
* @Flow\Inject
20+
* @var RedisContentCacheReader
21+
*/
22+
protected $redisContentCacheReader;
23+
24+
/**
25+
* @Flow\Inject
26+
* @var DocumentRenderer
27+
*/
28+
protected $documentRenderer;
29+
30+
31+
public function tryToExtractRenderingForEnumeratedNodeFromContentCache(EnumeratedNode $enumeratedNode): RenderedDocumentFromContentCache
32+
{
33+
return $this->redisContentCacheReader->tryToExtractRenderingForEnumeratedNodeFromContentCache(DocumentNodeCacheKey::fromEnumeratedNode($enumeratedNode));
34+
}
35+
36+
public function renderDocumentNodeVariant(NodeInterface $node, EnumeratedNode $enumeratedNode, ContentReleaseLogger $contentReleaseLogger)
37+
{
38+
return $this->documentRenderer->renderDocumentNodeVariant($node, $enumeratedNode->getArguments(), $contentReleaseLogger);
39+
}
40+
}

0 commit comments

Comments
 (0)