Skip to content

Commit 8b8ae1e

Browse files
committed
Merge branch 'main' into neos-9
* main: TASK: Ensure NodeTypeeNameSpecification does not deal with packages TASK: Add NodeObjectInterface to generated php classes TASK: Support wildcards and multiple packages seperated by comma # Conflicts: # Classes/Command/NodetypeObjectsCommandController.php # Classes/Domain/NodeTypeObjectNameSpecification.php # Tests/Unit/NodeTypeObjectNameSpecificationTest.php
2 parents e32a737 + d449dce commit 8b8ae1e

File tree

6 files changed

+178
-143
lines changed

6 files changed

+178
-143
lines changed

Classes/Command/NodetypeObjectsCommandController.php

Lines changed: 95 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -32,102 +32,119 @@ public function injectContentRepositoryRegistry(ContentRepositoryRegistry $conte
3232
}
3333

3434
/**
35-
* Remove all *NodeObject.php and *NodeInterface.php from the NodeTypes folder of the specified package
35+
* Remove all *NodeObject.php and *NodeInterface.php from the NodeTypes folder of the specified packages
3636
*
37-
* @param string $packageKey PackageKey to store the classes in
37+
* @param string $packageKeys PackageKey or Pattern, can be used seperated by comma
3838
* @return void
3939
*/
40-
public function cleanCommand(string $packageKey): void
40+
public function cleanCommand(string $packageKeys): void
4141
{
42-
if ($this->packageManager->isPackageAvailable($packageKey)) {
43-
$package = $this->packageManager->getPackage($packageKey);
44-
} else {
45-
$this->output->outputLine("Unknown package " . $packageKey);
46-
$this->quit(1);
47-
}
48-
if (!$package instanceof FlowPackageInterface) {
49-
$this->output->outputLine($packageKey . " is not a Flow package");
42+
$packages = $this->findPackagesByPackageKeyPattern($packageKeys);
43+
44+
if (empty($packages)) {
45+
$this->output->outputLine('No packages found for packageKeys <error>"%s"</error>:', [$packageKeys]);
5046
$this->quit(1);
47+
} else {
48+
$keys = array_map(fn (FlowPackageInterface $package) => $package->getPackageKey(), $packages);
49+
$this->output->outputLine('Removing NodeObjects and NodeInterfaces from packages <info>"%s"</info>:', [implode(', ', $keys)]);
5150
}
5251

53-
$packagePath = $package->getPackagePath();
54-
$files = Files::readDirectoryRecursively($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes', 'NodeObject.php');
55-
if (is_array($files)) {
56-
foreach ($files as $file) {
57-
if (is_file($file)) {
58-
unlink($file);
52+
foreach ($packages as $package) {
53+
$packagePath = $package->getPackagePath();
54+
if (!file_exists($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes')) {
55+
continue;
56+
}
57+
58+
$files = Files::readDirectoryRecursively($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes', 'NodeObject.php');
59+
if (is_array($files)) {
60+
foreach ($files as $file) {
61+
if (is_file($file)) {
62+
unlink($file);
63+
}
64+
$this->outputLine(' - ' . $file);
5965
}
60-
$this->outputLine(' - ' . $file);
6166
}
62-
}
63-
$files = Files::readDirectoryRecursively($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes', 'NodeInterface.php');
64-
if (is_array($files)) {
65-
foreach ($files as $file) {
66-
if (is_file($file)) {
67-
unlink($file);
67+
$files = Files::readDirectoryRecursively($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes', 'NodeInterface.php');
68+
if (is_array($files)) {
69+
foreach ($files as $file) {
70+
if (is_file($file)) {
71+
unlink($file);
72+
}
73+
$this->outputLine(' - ' . $file);
6874
}
69-
$this->outputLine(' - ' . $file);
7075
}
7176
}
7277
}
7378

7479
/**
7580
* Create new NodeTypeObjects for the selected Package
7681
*
77-
* @param string $packageKey PackageKey
82+
* @param string $packageKeys PackageKey or Pattern, can be used seperated comma
7883
*/
79-
public function buildCommand(string $packageKey, string $crId = 'default'): void
84+
public function buildCommand(string $packageKeys, string $crId = 'default'): void
8085
{
81-
$package = $this->getPackage($packageKey);
86+
$packages = $this->findPackagesByPackageKeyPattern($packageKeys);
87+
88+
if (empty($packages)) {
89+
$this->output->outputLine('No packages found for packageKeys <error>"%s"</error>:', [$packageKeys]);
90+
$this->quit(1);
91+
} else {
92+
$keys = array_map(fn (FlowPackageInterface $package) => $package->getPackageKey(), $packages);
93+
$this->output->outputLine('Building NodeObjects and NodeInterfaces for packages <info>"%s"</info>:', [implode(', ', $keys)]);
94+
}
8295

8396
$contentRepository = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($crId));
8497
$nodeTypeManager = $contentRepository->getNodeTypeManager();
85-
$nodeTypes = $nodeTypeManager->getNodeTypes();
98+
$nodeTypes = $nodeTypeManager->getNodeTypes(true);
8699
$nameSpecifications = [];
87-
88-
foreach ($nodeTypes as $nodeType) {
89-
if (!str_starts_with($nodeType->name->value, $packageKey . ':')) {
90-
continue;
100+
foreach ($packages as $package) {
101+
foreach ($nodeTypes as $nodeType) {
102+
if (!str_starts_with($nodeType->name->value, $package->getPackageKey() . ':')) {
103+
continue;
104+
}
105+
$nameSpecifications[$nodeType->name->value] = NodeTypeObjectNameSpecification::createFromNodeType($nodeType);
91106
}
92-
$nameSpecifications[$nodeType->name->value] = NodeTypeObjectNameSpecification::createFromPackageAndNodeType($package, $nodeType);
93107
}
94-
95108
$nameSpecificationsCollection = new NodeTypeObjectNameSpecificationCollection(...$nameSpecifications);
96109

97-
foreach ($nodeTypes as $nodeType) {
98-
if (!str_starts_with($nodeType->name->value, $packageKey . ':')) {
99-
continue;
100-
}
110+
// loop 2 build interfaces and objects
111+
foreach ($packages as $package) {
112+
foreach ($nodeTypes as $nodeType) {
113+
if (!str_starts_with($nodeType->name->value, $package->getPackageKey() . ':')) {
114+
continue;
115+
}
101116

102-
$specification = NodeTypeObjectSpecification::createFromPackageAndNodeType($package, $nodeType, $nameSpecificationsCollection);
117+
$specification = NodeTypeObjectSpecification::createFromPackageAndNodeType($package, $nodeType, $nameSpecificationsCollection);
103118

104-
Files::createDirectoryRecursively($specification->names->directory);
119+
Files::createDirectoryRecursively($specification->directory);
105120

106-
if ($specification->names->className) {
107-
file_put_contents(
108-
$specification->names->directory . DIRECTORY_SEPARATOR . $specification->names->className . '.php',
109-
$specification->toPhpClassString()
110-
);
111-
$this->outputLine(' - ' . $specification->names->nodeTypeName . ' -> ' . $specification->names->className);
112-
}
121+
$generatedFiles = [];
122+
if ($specification->classFilename) {
123+
file_put_contents(
124+
$specification->classFilename,
125+
$specification->toPhpClassString()
126+
);
127+
$generatedFiles[] = $specification->names->fullyQualifiedClassName;
128+
}
129+
if ($specification->interfaceFilename) {
130+
file_put_contents(
131+
$specification->interfaceFilename,
132+
$specification->toPhpInterfaceString()
133+
);
134+
$generatedFiles[] = $specification->names->fullyQualifiedInterfaceName;
135+
}
113136

114-
if ($specification->names->interfaceName) {
115-
file_put_contents(
116-
$specification->names->directory . DIRECTORY_SEPARATOR . $specification->names->interfaceName . '.php',
117-
$specification->toPhpInterfaceString()
118-
);
119-
$this->outputLine(' - ' . $specification->names->nodeTypeName . ' -> ' . $specification->names->interfaceName);
137+
$this->outputLine(' - ' . $specification->names->nodeTypeName . ' -> <info>' . implode(', ', $generatedFiles) . '</info>');
120138
}
121139
}
122140
}
123141

124142
/**
125143
* @param string $packageKey
126-
* @return FlowPackageInterface
127144
* @throws \Neos\Flow\Cli\Exception\StopCommandException
128145
* @throws \Neos\Flow\Package\Exception\UnknownPackageException
129146
*/
130-
protected function getPackage(string $packageKey): FlowPackageInterface
147+
protected function getPackage(string $packageKey): FlowPackageInterface & GenericPackage
131148
{
132149
if ($this->packageManager->isPackageAvailable($packageKey)) {
133150
$package = $this->packageManager->getPackage($packageKey);
@@ -167,7 +184,28 @@ protected function getPackage(string $packageKey): FlowPackageInterface
167184
$this->outputLine('<error>No PSR4-NodeTypes namespace for the NodeTypes folder is registered via composer</error>');
168185
$this->quit(1);
169186
}
170-
171187
return $package;
172188
}
189+
190+
/**
191+
* @param string $packageKeys
192+
* @return FlowPackageInterface[]
193+
*/
194+
protected function findPackagesByPackageKeyPattern(string $packageKeys): array
195+
{
196+
$packageKeyPatterns = explode(',', $packageKeys);
197+
198+
$allFlowPackages = $this->packageManager->getFlowPackages();
199+
200+
$packages = [];
201+
foreach ($allFlowPackages as $flowPackage) {
202+
foreach ($packageKeyPatterns as $packageKeyPattern) {
203+
if (fnmatch($packageKeyPattern, $flowPackage->getPackageKey())) {
204+
$packages[ $flowPackage->getPackageKey() ] = $flowPackage;
205+
break;
206+
}
207+
}
208+
}
209+
return $packages;
210+
}
173211
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PackageFactory\NodeTypeObjects\Domain;
6+
7+
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
8+
9+
interface NodeTypeObjectInterface
10+
{
11+
public static function fromNode(Node $node): self;
12+
}

Classes/Domain/NodeTypeObjectNameSpecification.php

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,48 @@
66

77
use Neos\ContentRepository\Core\NodeType\NodeType;
88
use Neos\Flow\Annotations as Flow;
9-
use Neos\Flow\Package\FlowPackageInterface;
109

1110
#[Flow\Proxy(false)]
1211
readonly class NodeTypeObjectNameSpecification
1312
{
1413
public function __construct(
15-
public string $directory,
1614
public string $nodeTypeName,
1715
public string $phpNamespace,
1816
public ?string $className,
19-
public ?string $interfaceName
17+
public ?string $fullyQualifiedClassName,
18+
public ?string $interfaceName,
19+
public ?string $fullyQualifiedInterfaceName,
2020
) {
2121
}
2222

23-
public static function createFromPackageAndNodeType(
24-
FlowPackageInterface $package,
23+
public static function createFromNodeType(
2524
NodeType $nodeType
2625
): self {
2726

28-
if (!str_starts_with($nodeType->name->value, $package->getPackageKey() . ':')) {
29-
throw new \Exception("Only nodetypes from the given package are allowed");
30-
}
27+
list($packageKey, $nodeName) = explode(':', $nodeType->name->value, 2);
3128

32-
$localNameParts = explode('.', str_replace($package->getPackageKey() . ':', '', $nodeType->name->value));
29+
$localNameParts = explode('.', $nodeName);
3330
$localName = array_pop($localNameParts);
34-
$localNamespace = implode('.', $localNameParts);
3531

36-
$namespace = str_replace('.', '\\', $package->getPackageKey())
37-
. '\\NodeTypes'
38-
. ($localNamespace ? '\\' . str_replace('.', '\\', $localNamespace) : '')
39-
. '\\' . $localName;
32+
$phpNamespace = str_replace(['.', ':'], ['\\', '\\NodeTypes\\'], $nodeType->name->value);
4033

41-
$directory = $package->getPackagePath()
42-
. 'NodeTypes' . DIRECTORY_SEPARATOR
43-
. ($localNamespace ? str_replace('.', DIRECTORY_SEPARATOR, $localNamespace) . DIRECTORY_SEPARATOR : '')
44-
. $localName;
4534

46-
if ($nodeType->getConfiguration('options.nodeTypeObjects.generateClass') === true) {
47-
$className = str_replace('.', '\\', $localName) . 'NodeObject';
48-
} else {
35+
if ($nodeType->isAbstract()) {
4936
$className = null;
50-
}
51-
if ($nodeType->getConfiguration('options.nodeTypeObjects.generateInterface') === true) {
52-
$interfaceName = str_replace('.', '\\', $localName) . 'NodeInterface';
5337
} else {
54-
$interfaceName = null;
38+
$className = str_replace('.', '\\', $localName) . 'NodeObject';
5539
}
5640

41+
/** @var string|null $interfaceName */
42+
$interfaceName = str_replace('.', '\\', $localName) . 'NodeInterface';
43+
5744
return new self(
58-
$directory,
5945
$nodeType->name->value,
60-
$namespace,
46+
$phpNamespace,
6147
$className,
48+
$className ? $phpNamespace . '\\' . $className : null,
6249
$interfaceName,
50+
$interfaceName ? $phpNamespace . '\\' . $interfaceName : null
6351
);
6452
}
6553
}

Classes/Domain/NodeTypeObjectSpecification.php

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public function __construct(
1515
public NodeTypeObjectNameSpecification $names,
1616
public NodePropertySpecificationCollection $properties,
1717
public NodeTypeObjectNameSpecificationCollection $superTypes,
18+
public string $directory,
19+
public ?string $classFilename,
20+
public ?string $interfaceFilename,
1821
) {
1922
}
2023

@@ -23,10 +26,32 @@ public static function createFromPackageAndNodeType(
2326
NodeType $nodeType,
2427
NodeTypeObjectNameSpecificationCollection $nameCollection
2528
): self {
26-
return new NodeTypeObjectSpecification(
27-
NodeTypeObjectNameSpecification::createFromPackageAndNodeType($package, $nodeType),
29+
30+
if (!str_starts_with($nodeType->name->value, $package->getPackageKey() . ':')) {
31+
throw new \Exception("Only nodetypes from the given package are allowed");
32+
}
33+
34+
$nameSpecification = NodeTypeObjectNameSpecification::createFromNodeType($nodeType);
35+
36+
$localNameParts = explode('.', str_replace($package->getPackageKey() . ':', '', $nodeType->name->value));
37+
$localName = array_pop($localNameParts);
38+
$localNamespace = implode('.', $localNameParts);
39+
40+
$directory = $package->getPackagePath()
41+
. 'NodeTypes' . DIRECTORY_SEPARATOR
42+
. ($localNamespace ? str_replace('.', DIRECTORY_SEPARATOR, $localNamespace) . DIRECTORY_SEPARATOR : '')
43+
. $localName;
44+
45+
$classFilename = $nameSpecification->className ? $directory . DIRECTORY_SEPARATOR . $nameSpecification->className . '.php' : null;
46+
$interfaceFileName = $nameSpecification->interfaceName ? $directory . DIRECTORY_SEPARATOR . $nameSpecification->interfaceName . '.php' : null;
47+
48+
return new self(
49+
$nameSpecification,
2850
NodePropertySpecificationCollection::createFromNodeType($nodeType),
29-
NodeTypeObjectNameSpecificationCollection::createFromNodeTypeAndCollection($nodeType, $nameCollection)
51+
NodeTypeObjectNameSpecificationCollection::createFromNodeTypeAndCollection($nodeType, $nameCollection),
52+
$directory,
53+
$classFilename,
54+
$interfaceFileName
3055
);
3156
}
3257

@@ -55,12 +80,9 @@ public function toPhpClassString(): ?string
5580
}
5681
}
5782

58-
if (count($interfaceNames) > 0) {
59-
$interfaceDeclaration = 'implements ' . implode(', ', $interfaceNames);
60-
} else {
61-
$interfaceDeclaration = '';
62-
}
83+
$interfaceNames[] = '\\' . NodeTypeObjectInterface::class;
6384

85+
$interfaceDeclaration = 'implements ' . implode(', ', $interfaceNames);
6486

6587
$class = <<<EOL
6688
<?php
@@ -69,7 +91,7 @@ public function toPhpClassString(): ?string
6991
7092
namespace {$this->names->phpNamespace};
7193
72-
use Neos\ContentRepository\Domain\Model\NodeInterface;
94+
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
7395
use Neos\Flow\Annotations as Flow;
7496
7597
/**
@@ -81,14 +103,14 @@ public function toPhpClassString(): ?string
81103
final readonly class {$this->names->className} {$interfaceDeclaration}
82104
{
83105
private function __construct(
84-
public NodeInterface \$node
106+
public Node \$node
85107
) {
86108
}
87109
88-
public static function fromNode(NodeInterface \$node): self
110+
public static function fromNode(Node \$node): self
89111
{
90-
if (\$node->getNodeType()->getName() !== "{$this->names->nodeTypeName}") {
91-
throw new \Exception("unsupported nodetype " . \$node->getNodeType()->getName());
112+
if (\$node->nodeTypeName->value !== "{$this->names->nodeTypeName}") {
113+
throw new \Exception("unsupported nodetype " . \$node->nodeTypeName->value);
92114
}
93115
return new self(\$node);
94116
}

0 commit comments

Comments
 (0)