-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #63 from grebaldi/feature/tree-cqrs
BUGFIX: Refactor node tree using Query DTOs
- Loading branch information
Showing
80 changed files
with
3,349 additions
and
1,307 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
indent_style = space | ||
indent_size = 4 | ||
|
||
[*.{yaml,yml}] | ||
indent_size = 2 | ||
|
||
[{package.json,.babelrc,.eslintrc,.stylelintrc}] | ||
indent_size = 2 | ||
|
||
[Makefile] | ||
indent_style = tab | ||
|
||
[*.makefile] | ||
indent_style = tab | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false | ||
|
||
[*.yaml.example] | ||
indent_size = 2 |
39 changes: 39 additions & 0 deletions
39
Classes/Application/GetChildrenForTreeNode/Controller/GetChildrenForTreeNodeController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode\Controller; | ||
|
||
use Neos\Flow\Annotations as Flow; | ||
use Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode\GetChildrenForTreeNodeQuery; | ||
use Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode\GetChildrenForTreeNodeQueryHandler; | ||
use Sitegeist\Archaeopteryx\Application\Shared\NodeWasNotFound; | ||
use Sitegeist\Archaeopteryx\Framework\MVC\QueryController; | ||
use Sitegeist\Archaeopteryx\Framework\MVC\QueryResponse; | ||
|
||
#[Flow\Scope("singleton")] | ||
final class GetChildrenForTreeNodeController extends QueryController | ||
{ | ||
#[Flow\Inject] | ||
protected GetChildrenForTreeNodeQueryHandler $queryHandler; | ||
|
||
public function processQuery(array $arguments): QueryResponse | ||
{ | ||
try { | ||
$query = GetChildrenForTreeNodeQuery::fromArray($arguments); | ||
$queryResult = $this->queryHandler->handle($query); | ||
|
||
return QueryResponse::success($queryResult); | ||
} catch (NodeWasNotFound $e) { | ||
return QueryResponse::clientError($e); | ||
} | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
Classes/Application/GetChildrenForTreeNode/GetChildrenForTreeNodeQuery.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode; | ||
|
||
use Neos\ContentRepository\Domain\NodeAggregate\NodeAggregateIdentifier; | ||
use Neos\ContentRepository\Domain\NodeType\NodeTypeName; | ||
use Neos\Flow\Annotations as Flow; | ||
use Sitegeist\Archaeopteryx\Application\Shared\NodeTypeNames; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Flow\Proxy(false)] | ||
final class GetChildrenForTreeNodeQuery | ||
{ | ||
/** | ||
* @param array<string,array<int,string>> $dimensionValues | ||
*/ | ||
public function __construct( | ||
public readonly string $workspaceName, | ||
public readonly array $dimensionValues, | ||
public readonly NodeAggregateIdentifier $treeNodeId, | ||
public readonly string $nodeTypeFilter, | ||
public readonly NodeTypeNames $linkableNodeTypes, | ||
) { | ||
} | ||
|
||
/** | ||
* @param array<string,mixed> $array | ||
*/ | ||
public static function fromArray(array $array): self | ||
{ | ||
isset($array['workspaceName']) | ||
or throw new \InvalidArgumentException('Workspace name must be set'); | ||
is_string($array['workspaceName']) | ||
or throw new \InvalidArgumentException('Workspace name must be a string'); | ||
|
||
isset($array['dimensionValues']) | ||
or throw new \InvalidArgumentException('Dimension values must be set'); | ||
is_array($array['dimensionValues']) | ||
or throw new \InvalidArgumentException('Dimension values must be an array'); | ||
|
||
isset($array['treeNodeId']) | ||
or throw new \InvalidArgumentException('Tree node id must be set'); | ||
is_string($array['treeNodeId']) | ||
or throw new \InvalidArgumentException('Tree node id must be a string'); | ||
|
||
!isset($array['nodeTypeFilter']) or is_string($array['nodeTypeFilter']) | ||
or throw new \InvalidArgumentException('Node type filter must be a string'); | ||
|
||
!isset($array['linkableNodeTypes']) or is_array($array['linkableNodeTypes']) | ||
or throw new \InvalidArgumentException('Linkable node types must be an array'); | ||
|
||
return new self( | ||
workspaceName: $array['workspaceName'], | ||
dimensionValues: $array['dimensionValues'], | ||
treeNodeId: NodeAggregateIdentifier::fromString($array['treeNodeId']), | ||
nodeTypeFilter: $array['nodeTypeFilter'] ?? '', | ||
linkableNodeTypes: NodeTypeNames::fromArray($array['linkableNodeTypes'] ?? []), | ||
); | ||
} | ||
|
||
/** | ||
* @return array<string,string> | ||
*/ | ||
public function getTargetDimensionValues(): array | ||
{ | ||
$result = []; | ||
|
||
foreach ($this->dimensionValues as $dimensionName => $fallbackChain) { | ||
$result[$dimensionName] = $fallbackChain[0] ?? ''; | ||
} | ||
|
||
return $result; | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
Classes/Application/GetChildrenForTreeNode/GetChildrenForTreeNodeQueryHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode; | ||
|
||
use Neos\ContentRepository\Domain\Model\Node; | ||
use Neos\ContentRepository\Domain\Service\NodeTypeManager; | ||
use Neos\Flow\Annotations as Flow; | ||
use Neos\Neos\Domain\Service\ContentContextFactory; | ||
use Sitegeist\Archaeopteryx\Application\Shared\NodeWasNotFound; | ||
use Sitegeist\Archaeopteryx\Application\Shared\TreeNodeBuilder; | ||
use Sitegeist\Archaeopteryx\Application\Shared\TreeNodes; | ||
use Sitegeist\Archaeopteryx\Infrastructure\ContentRepository\NodeTypeFilter; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Flow\Scope("singleton")] | ||
final class GetChildrenForTreeNodeQueryHandler | ||
{ | ||
#[Flow\Inject] | ||
protected ContentContextFactory $contentContextFactory; | ||
|
||
#[Flow\Inject] | ||
protected NodeTypeManager $nodeTypeManager; | ||
|
||
public function handle(GetChildrenForTreeNodeQuery $query): GetChildrenForTreeNodeQueryResult | ||
{ | ||
$contentContext = $this->contentContextFactory->create([ | ||
'workspaceName' => $query->workspaceName, | ||
'dimensions' => $query->dimensionValues, | ||
'targetDimensions' => $query->getTargetDimensionValues(), | ||
'invisibleContentShown' => true, | ||
'removedContentShown' => false, | ||
'inaccessibleContentShown' => true | ||
]); | ||
|
||
$node = $contentContext->getNodeByIdentifier((string) $query->treeNodeId); | ||
if (!$node instanceof Node) { | ||
throw NodeWasNotFound::becauseNodeWithGivenIdentifierDoesNotExistInContext( | ||
nodeAggregateIdentifier: $query->treeNodeId, | ||
contentContext: $contentContext, | ||
); | ||
} | ||
|
||
return new GetChildrenForTreeNodeQueryResult( | ||
children: $this->createTreeNodesFromChildrenOfNode($node, $query), | ||
); | ||
} | ||
|
||
private function createTreeNodesFromChildrenOfNode(Node $node, GetChildrenForTreeNodeQuery $query): TreeNodes | ||
{ | ||
$linkableNodeTypesFilter = NodeTypeFilter::fromNodeTypeNames( | ||
nodeTypeNames: $query->linkableNodeTypes, | ||
nodeTypeManager: $this->nodeTypeManager | ||
); | ||
|
||
$items = []; | ||
|
||
foreach ($node->getChildNodes($query->nodeTypeFilter) as $childNode) { | ||
/** @var Node $childNode */ | ||
$items[] = TreeNodeBuilder::forNode($childNode) | ||
->setIsMatchedByFilter(true) | ||
->setIsLinkable($linkableNodeTypesFilter->isSatisfiedByNode($childNode)) | ||
->setHasUnloadedChildren($childNode->getNumberOfChildNodes($query->nodeTypeFilter) > 0) | ||
->build(); | ||
} | ||
|
||
return new TreeNodes(...$items); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
Classes/Application/GetChildrenForTreeNode/GetChildrenForTreeNodeQueryResult.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetChildrenForTreeNode; | ||
|
||
use Neos\Flow\Annotations as Flow; | ||
use Sitegeist\Archaeopteryx\Application\Shared\TreeNodes; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Flow\Proxy(false)] | ||
final class GetChildrenForTreeNodeQueryResult implements \JsonSerializable | ||
{ | ||
public function __construct( | ||
public readonly TreeNodes $children, | ||
) { | ||
} | ||
|
||
public function jsonSerialize(): mixed | ||
{ | ||
return get_object_vars($this); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetNodeSummary; | ||
|
||
use Neos\Flow\Annotations as Flow; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Flow\Proxy(false)] | ||
final class Breadcrumb implements \JsonSerializable | ||
{ | ||
public function __construct( | ||
public readonly string $icon, | ||
public readonly string $label, | ||
) { | ||
} | ||
|
||
public function jsonSerialize(): mixed | ||
{ | ||
return get_object_vars($this); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetNodeSummary; | ||
|
||
use Neos\Flow\Annotations as Flow; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Flow\Proxy(false)] | ||
final class Breadcrumbs implements \JsonSerializable | ||
{ | ||
/** @var Breadcrumb[] */ | ||
private readonly array $items; | ||
|
||
public function __construct(Breadcrumb ...$items) | ||
{ | ||
$this->items = array_values($items); | ||
} | ||
|
||
public function jsonSerialize(): mixed | ||
{ | ||
return $this->items; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
Classes/Application/GetNodeSummary/Controller/GetNodeSummaryController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
/* | ||
* This script belongs to the package "Sitegeist.Archaeopteryx". | ||
* | ||
* This package is Open Source Software. For the full copyright and license | ||
* information, please view the LICENSE file which was distributed with this | ||
* source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Sitegeist\Archaeopteryx\Application\GetNodeSummary\Controller; | ||
|
||
use Neos\Flow\Annotations as Flow; | ||
use Sitegeist\Archaeopteryx\Application\GetNodeSummary\GetNodeSummaryQuery; | ||
use Sitegeist\Archaeopteryx\Application\GetNodeSummary\GetNodeSummaryQueryHandler; | ||
use Sitegeist\Archaeopteryx\Application\Shared\NodeWasNotFound; | ||
use Sitegeist\Archaeopteryx\Framework\MVC\QueryController; | ||
use Sitegeist\Archaeopteryx\Framework\MVC\QueryResponse; | ||
|
||
#[Flow\Scope("singleton")] | ||
final class GetNodeSummaryController extends QueryController | ||
{ | ||
#[Flow\Inject] | ||
protected GetNodeSummaryQueryHandler $queryHandler; | ||
|
||
public function processQuery(array $arguments): QueryResponse | ||
{ | ||
try { | ||
$query = GetNodeSummaryQuery::fromArray($arguments); | ||
$queryResult = $this->queryHandler->handle($query); | ||
|
||
return QueryResponse::success($queryResult); | ||
} catch (NodeWasNotFound $e) { | ||
return QueryResponse::clientError($e); | ||
} | ||
} | ||
} |
Oops, something went wrong.