Skip to content

Commit b9890d6

Browse files
authored
Merge pull request #17 from Flowpack/feature-enumerated-node-signal
FEATURE: Add signal for enumerated node for enumerating additional nodes with arguments
2 parents a239758 + 6c6a136 commit b9890d6

6 files changed

Lines changed: 115 additions & 20 deletions

File tree

Classes/Core/Infrastructure/ContentReleaseLogger.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,22 @@ public static function fromSymfonyOutput(OutputInterface $output, ContentRelease
4949

5050
public function debug($message, array $additionalPayload = [])
5151
{
52-
$this->output->writeln($this->logPrefix . $message . json_encode($additionalPayload));
52+
$this->output->writeln($this->logPrefix . 'DEBUG ' . $message . ($additionalPayload ? ' ' . json_encode($additionalPayload) : ''));
5353
}
5454

5555
public function info($message, array $additionalPayload = [])
5656
{
57-
$this->output->writeln($this->logPrefix . $message . json_encode($additionalPayload));
57+
$this->output->writeln($this->logPrefix . 'INFO ' . $message . ($additionalPayload ? ' ' . json_encode($additionalPayload) : ''));
5858
}
5959

6060
public function warn($message, array $additionalPayload = [])
6161
{
62-
$this->output->writeln($this->logPrefix . $message . json_encode($additionalPayload));
62+
$this->output->writeln($this->logPrefix . 'WARN ' . $message . ($additionalPayload ? ' ' . json_encode($additionalPayload) : ''));
6363
}
6464

6565
public function error($message, array $additionalPayload = [])
6666
{
67-
$this->output->writeln($this->logPrefix . $message . json_encode($additionalPayload));
67+
$this->output->writeln($this->logPrefix . 'ERROR ' . $message . ($additionalPayload ? ' ' . json_encode($additionalPayload) : ''));
6868
}
6969

7070
public function logException(\Exception $exception, string $message, array $additionalPayload)

Classes/NodeEnumeration/Domain/Dto/EnumeratedNode.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,24 @@ final class EnumeratedNode implements \JsonSerializable
3434
*/
3535
protected $arguments;
3636

37-
private function __construct(string $contextPath, string $nodeIdentifier, array $arguments)
37+
/**
38+
* The node type name
39+
*
40+
* @var string
41+
*/
42+
protected $nodeTypeName;
43+
44+
private function __construct(string $contextPath, string $nodeIdentifier, string $nodeTypeName, array $arguments)
3845
{
3946
$this->contextPath = $contextPath;
4047
$this->nodeIdentifier = $nodeIdentifier;
48+
$this->nodeTypeName = $nodeTypeName;
4149
$this->arguments = $arguments;
4250
}
4351

44-
45-
static public function fromNode(NodeInterface $node): self
52+
static public function fromNode(NodeInterface $node, array $arguments = []): self
4653
{
47-
return new self($node->getContextPath(), $node->getIdentifier(), []);
54+
return new self($node->getContextPath(), $node->getIdentifier(), $node->getNodeType()->getName(), $arguments);
4855
}
4956

5057
static public function fromJsonString(string $enumeratedNodeString): self
@@ -53,14 +60,15 @@ static public function fromJsonString(string $enumeratedNodeString): self
5360
if (!is_array($tmp)) {
5461
throw new \Exception('EnumeratedNode cannot be constructed from: ' . $enumeratedNodeString);
5562
}
56-
return new self($tmp['contextPath'], $tmp['nodeIdentifier'], $tmp['arguments']);
63+
return new self($tmp['contextPath'], $tmp['nodeIdentifier'], $tmp['nodeTypeName'] ?? '', $tmp['arguments']);
5764
}
5865

5966
public function jsonSerialize()
6067
{
6168
return [
6269
'contextPath' => $this->contextPath,
6370
'nodeIdentifier' => $this->nodeIdentifier,
71+
'nodeTypeName' => $this->nodeTypeName,
6472
'arguments' => $this->arguments
6573
];
6674
}
@@ -80,21 +88,23 @@ public function getDimensionsFromContextPath(): array
8088
return $nodePathAndContext['dimensions'];
8189
}
8290

83-
/**
84-
* @return string
85-
*/
8691
public function getNodeIdentifier(): string
8792
{
8893
return $this->nodeIdentifier;
8994
}
9095

96+
public function getNodeTypeName(): string
97+
{
98+
return $this->nodeTypeName;
99+
}
100+
91101
public function getArguments(): array
92102
{
93103
return $this->arguments;
94104
}
95105

96106
public function debugString(): string
97107
{
98-
return sprintf('Node %s (%s)', $this->nodeIdentifier, $this->contextPath);
108+
return sprintf('%s %s %s(%s)', $this->nodeTypeName, $this->nodeIdentifier, $this->arguments ? http_build_query($this->arguments) . ' ' : '', $this->contextPath);
99109
}
100-
}
110+
}

Classes/NodeEnumeration/NodeEnumerator.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ public function enumerateAndStoreInRedis(?Site $site, ContentReleaseLogger $cont
6767
// TODO: EXTENSION POINT HERE, TO ADD ADDITIONAL ENUMERATIONS (.metadata.json f.e.)
6868
// TODO: not yet fully sure how to handle Enumeration
6969
$this->redisEnumerationRepository->addDocumentNodesToEnumeration($releaseIdentifier, ...$enumeration);
70+
foreach ($enumeration as $enumeratedNode) {
71+
$this->emitNodeEnumerated($enumeratedNode, $releaseIdentifier, $contentReleaseLogger);
72+
}
7073
}
7174
}
7275

7376
/**
74-
* @param Site $site
77+
* @param Site|null $site
7578
* @param ContentReleaseLogger $contentReleaseLogger
7679
* @return iterable<EnumeratedNode>
7780
*/
@@ -97,12 +100,13 @@ private function enumerateAll(?Site $site, ContentReleaseLogger $contentReleaseL
97100
$contextPath = $documentNode->getContextPath();
98101

99102
if ($nodeTypeWhitelist->matches(NodeTypeName::fromString($documentNode->getNodeType()->getName()))) {
103+
$enumeratedNode = EnumeratedNode::fromNode($documentNode);
100104

101105
$contentReleaseLogger->debug('Registering node for publishing', [
102-
'node' => $contextPath
106+
'enumeratedNode' => $enumeratedNode->debugString()
103107
]);
104108

105-
yield EnumeratedNode::fromNode($documentNode);
109+
yield $enumeratedNode;
106110
} else {
107111
$contentReleaseLogger->debug('Skipping node from publishing, because it did not match the configured nodeTypeWhitelist', [
108112
'node' => $contextPath,
@@ -123,4 +127,19 @@ private function enumerateAll(?Site $site, ContentReleaseLogger $contentReleaseL
123127
}
124128
}
125129

130+
/**
131+
* A node was enumerated for a new content release.
132+
*
133+
* This signal can be used to add additional EnumeratedNode entries (e.g. with added arguments for pagination or filters) based on the given node.
134+
*
135+
* @param EnumeratedNode $enumeratedNode
136+
* @param ContentReleaseIdentifier $releaseIdentifier
137+
* @param ContentReleaseLogger $contentReleaseLogger
138+
* @return void
139+
* @Flow\Signal
140+
*/
141+
protected function emitNodeEnumerated(EnumeratedNode $enumeratedNode, ContentReleaseIdentifier $releaseIdentifier, ContentReleaseLogger $contentReleaseLogger)
142+
{
143+
}
144+
126145
}

Classes/NodeRendering/Render/DocumentRenderer.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
2+
23
namespace Flowpack\DecoupledContentStore\NodeRendering\Render;
4+
35
use Flowpack\DecoupledContentStore\Aspects\CacheUrlMappingAspect;
46
use Flowpack\DecoupledContentStore\Exception;
57
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
@@ -154,7 +156,7 @@ protected function buildControllerContextAndSetBaseUri(string $uri, NodeInterfac
154156
return new ControllerContext(
155157
$request,
156158
new ActionResponse(),
157-
new Arguments(array()),
159+
new Arguments([]),
158160
$uriBuilder
159161
);
160162
}
@@ -227,7 +229,7 @@ protected function renderDocumentView(NodeInterface $node, $uri, array $requestA
227229
);
228230
}
229231

230-
$contentReleaseLogger->debug('Rendering document for URI ' . $uri, ['baseUri' => $baseUri]);
232+
$contentReleaseLogger->info('Rendering document for URI ' . $uri, ['baseUri' => $baseUri]);
231233

232234
$controllerContext = $this->buildControllerContextAndSetBaseUri($uri, $node, $requestArguments);
233235
/** @var ActionRequest $request */

Classes/Utility/GeneratorUtility.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
class GeneratorUtility
66
{
77

8+
/**
9+
* @template T
10+
* @param iterable<T> $iterable
11+
* @param int $chunkSize
12+
* @return iterable<array<T>>
13+
*/
814
static public function createArrayBatch(iterable $iterable, int $chunkSize): iterable
915
{
1016
$accumulator = [];
@@ -24,4 +30,4 @@ static public function createArrayBatch(iterable $iterable, int $chunkSize): ite
2430
yield $accumulator;
2531
}
2632
}
27-
}
33+
}

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,64 @@ Flowpack:
338338
This is needed so that the system knows which keys should be synchronized between the different content stores,
339339
and what data to delete if a release is removed.
340340

341+
### Rendering additional nodes with arguments (e.g. pagination or filters)
342+
343+
If you render a paginated list or have filters (with a predictable list of values) that can be
344+
added to a document via arguments, you can implement a slot for the `nodeEnumerated` signal to enumerate additional
345+
nodes with arguments.
346+
347+
> **Note:** Request arguments must be mapped to URIs via custom routes, since we do not support HTTP query parameters for rendered documents.
348+
349+
#### Example
350+
351+
Add a slot for the `nodeEnumerated` signal via `Package.php`:
352+
353+
```php
354+
<?php
355+
class Package extends BasePackage
356+
{
357+
public function boot(Bootstrap $bootstrap)
358+
{
359+
$dispatcher = $bootstrap->getSignalSlotDispatcher();
360+
361+
$dispatcher->connect(NodeEnumerator::class, 'nodeEnumerated', MyNodeListsEnumerator::class, 'enumerateNodeLists');
362+
}
363+
}
364+
```
365+
366+
Implement the slot and enumerate additional nodes depending on the node type:
367+
368+
```php
369+
<?php
370+
class NodeListsEnumerator
371+
{
372+
public function enumerateNodeLists(EnumeratedNode $enumeratedNode, ContentReleaseIdentifier $releaseIdentifier, ContentReleaseLogger $logger)
373+
{
374+
$nodeTypeName = $enumeratedNode->getNodeTypeName();
375+
$nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName);
376+
if ($nodeType->isOfType('Vendor.Site:Document.Blog.Folder')) {
377+
// Get the node and count the number of pages to render
378+
// $pageCount = ...
379+
380+
$pageCount = ceil($postCount / (float)$this->perPage);
381+
if ($pageCount <= 1) {
382+
return;
383+
}
384+
385+
// Start after the first page, because the first page will be the document without arguments
386+
for ($page = 2; $page <= $pageCount; $page++) {
387+
$enumeratedNodes[] = EnumeratedNode::fromNode($documentNode, ['page' => $page]);
388+
}
389+
390+
$this->redisEnumerationRepository->addDocumentNodesToEnumeration($releaseIdentifier, ...$enumeratedNodes);
391+
}
392+
}
393+
}
394+
```
395+
396+
The actual logic will depend on your use of the node. Having the actual filtering logic implemented in PHP is
397+
beneficial, because it allows you to use it in the rendering process as well as in the additional enumeration.
398+
341399
### Extending the backend module
342400

343401
- You need a Views.yaml in your package, looking like this:

0 commit comments

Comments
 (0)