Skip to content

Commit e32a737

Browse files
committed
Merge branch 'main' into neos-9
* main: TASK: Refactoring and first unit tests # Conflicts: # Classes/Command/NodetypeObjectsCommandController.php # Classes/Factory/NodeTypeObjectFileFactory.php # Classes/Factory/NodeTypeSpecificationFactory.php
2 parents 08db2fd + 2697f13 commit e32a737

13 files changed

+576
-297
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
php-versions: ["8.2", "8.3"]
21-
neos-versions: ["9.0.0-beta18"]
21+
neos-versions: ["9.0"]
2222

2323
runs-on: ubuntu-latest
2424

@@ -36,9 +36,11 @@ jobs:
3636
ini-values: date.timezone="Africa/Tunis", opcache.fast_shutdown=0, apc.enable_cli=on
3737

3838
- name: Set Neos Version
39-
run: >
40-
composer config minimum-stability beta &&
41-
composer require neos/neos ^${{ matrix.neos-versions }} --no-progress --no-interaction
39+
run: |
40+
composer require neos/contentgraph-doctrinedbaladapter ^${{ matrix.neos-versions }} --no-progress --no-interaction --no-update
41+
composer require neos/eventstore-doctrineadapter --no-progress --no-interaction --no-update
42+
composer require neos/neos ^${{ matrix.neos-versions }} --no-progress --no-interaction --no-update
43+
composer update
4244
4345
- name: Run Linter
4446
run: composer lint

Classes/Command/NodetypeObjectsCommandController.php

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
use Neos\Flow\Package\GenericPackage;
1212
use Neos\Flow\Package\PackageManager;
1313
use Neos\Utility\Files;
14-
use PackageFactory\NodeTypeObjects\Factory\NodeTypeObjectFileFactory;
15-
use PackageFactory\NodeTypeObjects\Factory\NodeTypeSpecificationFactory;
14+
use PackageFactory\NodeTypeObjects\Domain\NodeTypeObjectNameSpecification;
15+
use PackageFactory\NodeTypeObjects\Domain\NodeTypeObjectNameSpecificationCollection;
16+
use PackageFactory\NodeTypeObjects\Domain\NodeTypeObjectSpecification;
1617

1718
class NodetypeObjectsCommandController extends CommandController
1819
{
1920
private PackageManager $packageManager;
20-
private NodeTypeSpecificationFactory $nodeTypeSpecificationFactory;
21-
private NodeTypeObjectFileFactory $nodeTypeObjectFileFactory;
21+
2222
private ContentRepositoryRegistry $contentRepositoryRegistry;
2323

2424
public function injectPackageManager(PackageManager $packageManager): void
@@ -31,19 +31,8 @@ public function injectContentRepositoryRegistry(ContentRepositoryRegistry $conte
3131
$this->contentRepositoryRegistry = $contentRepositoryRegistry;
3232
}
3333

34-
35-
public function injectNodeTypeSpecificationFactory(NodeTypeSpecificationFactory $nodeTypeSpecificationFactory): void
36-
{
37-
$this->nodeTypeSpecificationFactory = $nodeTypeSpecificationFactory;
38-
}
39-
40-
public function injectNodeTypeObjectFileFactory(NodeTypeObjectFileFactory $nodeTypeObjectFileFactory): void
41-
{
42-
$this->nodeTypeObjectFileFactory = $nodeTypeObjectFileFactory;
43-
}
44-
4534
/**
46-
* Remove all NodeTypeObjects from the selected package
35+
* Remove all *NodeObject.php and *NodeInterface.php from the NodeTypes folder of the specified package
4736
*
4837
* @param string $packageKey PackageKey to store the classes in
4938
* @return void
@@ -62,7 +51,16 @@ public function cleanCommand(string $packageKey): void
6251
}
6352

6453
$packagePath = $package->getPackagePath();
65-
$files = Files::readDirectoryRecursively($packagePath, 'NodeObject.php');
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);
59+
}
60+
$this->outputLine(' - ' . $file);
61+
}
62+
}
63+
$files = Files::readDirectoryRecursively($packagePath . DIRECTORY_SEPARATOR . 'NodeTypes', 'NodeInterface.php');
6664
if (is_array($files)) {
6765
foreach ($files as $file) {
6866
if (is_file($file)) {
@@ -79,6 +77,57 @@ public function cleanCommand(string $packageKey): void
7977
* @param string $packageKey PackageKey
8078
*/
8179
public function buildCommand(string $packageKey, string $crId = 'default'): void
80+
{
81+
$package = $this->getPackage($packageKey);
82+
83+
$contentRepository = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($crId));
84+
$nodeTypeManager = $contentRepository->getNodeTypeManager();
85+
$nodeTypes = $nodeTypeManager->getNodeTypes();
86+
$nameSpecifications = [];
87+
88+
foreach ($nodeTypes as $nodeType) {
89+
if (!str_starts_with($nodeType->name->value, $packageKey . ':')) {
90+
continue;
91+
}
92+
$nameSpecifications[$nodeType->name->value] = NodeTypeObjectNameSpecification::createFromPackageAndNodeType($package, $nodeType);
93+
}
94+
95+
$nameSpecificationsCollection = new NodeTypeObjectNameSpecificationCollection(...$nameSpecifications);
96+
97+
foreach ($nodeTypes as $nodeType) {
98+
if (!str_starts_with($nodeType->name->value, $packageKey . ':')) {
99+
continue;
100+
}
101+
102+
$specification = NodeTypeObjectSpecification::createFromPackageAndNodeType($package, $nodeType, $nameSpecificationsCollection);
103+
104+
Files::createDirectoryRecursively($specification->names->directory);
105+
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+
}
113+
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);
120+
}
121+
}
122+
}
123+
124+
/**
125+
* @param string $packageKey
126+
* @return FlowPackageInterface
127+
* @throws \Neos\Flow\Cli\Exception\StopCommandException
128+
* @throws \Neos\Flow\Package\Exception\UnknownPackageException
129+
*/
130+
protected function getPackage(string $packageKey): FlowPackageInterface
82131
{
83132
if ($this->packageManager->isPackageAvailable($packageKey)) {
84133
$package = $this->packageManager->getPackage($packageKey);
@@ -102,14 +151,14 @@ public function buildCommand(string $packageKey, string $crId = 'default'): void
102151
$namespace = null;
103152
foreach ($autoloadConfigurations as $autoloadConfiguration) {
104153
if (
105-
$autoloadConfiguration['mappingType'] === 'psr-4'
106-
&& str_ends_with($autoloadConfiguration['namespace'], '\\NodeTypes\\')
154+
$autoloadConfiguration[ 'mappingType' ] === 'psr-4'
155+
&& str_ends_with($autoloadConfiguration[ 'namespace' ], '\\NodeTypes\\')
107156
&& (
108-
$autoloadConfiguration['classPath'] === $package->getPackagePath() . 'NodeTypes'
109-
|| $autoloadConfiguration['classPath'] === $package->getPackagePath() . 'NodeTypes/'
157+
$autoloadConfiguration[ 'classPath' ] === $package->getPackagePath() . 'NodeTypes'
158+
|| $autoloadConfiguration[ 'classPath' ] === $package->getPackagePath() . 'NodeTypes/'
110159
)
111160
) {
112-
$namespace = $autoloadConfiguration['namespace'];
161+
$namespace = $autoloadConfiguration[ 'namespace' ];
113162
break;
114163
}
115164
}
@@ -119,29 +168,6 @@ public function buildCommand(string $packageKey, string $crId = 'default'): void
119168
$this->quit(1);
120169
}
121170

122-
$contentRepository = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($crId));
123-
$nodeTypeManager = $contentRepository->getNodeTypeManager();
124-
$nodeTypes = $nodeTypeManager->getNodeTypes(false);
125-
foreach ($nodeTypes as $nodeType) {
126-
if (!str_starts_with($nodeType->name->value, $packageKey . ':')) {
127-
continue;
128-
}
129-
130-
$specification = $this->nodeTypeSpecificationFactory->createFromPackageKeyAndNodeType(
131-
$package,
132-
$nodeType
133-
);
134-
135-
$nodeTypeObjectFile = $this->nodeTypeObjectFileFactory->createNodeTypeObjectPhpCodeFromNode($specification);
136-
137-
Files::createDirectoryRecursively($nodeTypeObjectFile->pathName);
138-
139-
file_put_contents(
140-
$nodeTypeObjectFile->fileNameWithPath,
141-
$nodeTypeObjectFile->fileContent
142-
);
143-
144-
$this->outputLine(' - ' . $specification->nodeTypeName . ' -> ' . $specification->className);
145-
}
171+
return $package;
146172
}
147173
}

Classes/Domain/NodePropertySpecification.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PackageFactory\NodeTypeObjects\Domain;
66

77
use Neos\Flow\Annotations as Flow;
8+
use Neos\Utility\Unicode\Functions as UnicodeFunctions;
89

910
#[Flow\Proxy(false)]
1011
readonly class NodePropertySpecification
@@ -15,4 +16,142 @@ public function __construct(
1516
public mixed $defaultValue,
1617
) {
1718
}
19+
20+
public function getPhpType(): string
21+
{
22+
$phpType = $this->propertyType;
23+
24+
if (str_ends_with($this->propertyType, '[]')) {
25+
$phpType = 'array';
26+
} elseif (str_starts_with($this->propertyType, 'array<') && str_ends_with($this->propertyType, '>')) {
27+
$phpType = 'array';
28+
} elseif ($this->propertyType === 'boolean') {
29+
$phpType = 'bool';
30+
} elseif ($this->propertyType === 'integer') {
31+
$phpType = 'int';
32+
} elseif ($this->propertyType === 'DateTime') {
33+
$phpType = '\DateTime';
34+
}
35+
36+
if (str_contains($phpType, '\\') && !str_starts_with($phpType, '\\')) {
37+
$phpType = '\\' . $phpType;
38+
}
39+
40+
return $phpType;
41+
}
42+
43+
public function getAnnotationType(): ?string
44+
{
45+
$annotationType = null;
46+
47+
if (str_ends_with($this->propertyType, '[]')) {
48+
$annotationType = $this->propertyType;
49+
} elseif (str_starts_with($this->propertyType, 'array<') && str_ends_with($this->propertyType, '>')) {
50+
$annotationType = substr($this->propertyType, 6, -1) . '[]';
51+
}
52+
53+
if (is_string($annotationType) && str_contains($annotationType, '\\') && !str_starts_with($annotationType, '\\')) {
54+
$annotationType = '\\' . $annotationType;
55+
}
56+
57+
return $annotationType;
58+
}
59+
60+
public function toPhpClassMethodString(): string
61+
{
62+
$propertyIsInternal = str_starts_with($this->propertyName, '_');
63+
if ($propertyIsInternal) {
64+
$methodName = 'getInternal' . UnicodeFunctions::ucfirst(substr($this->propertyName, 1));
65+
} else {
66+
$methodName = 'get' . UnicodeFunctions::ucfirst($this->propertyName);
67+
}
68+
69+
$annotationType = $this->getAnnotationType();
70+
$phpType = $this->getPhpType();
71+
72+
$returnType = ($this->defaultValue === null) ? '?' . $phpType : $phpType;
73+
74+
$defaultReturn = match (true) {
75+
($this->propertyType === 'DateTime' && is_string($this->defaultValue)) => 'new \DateTime(\'' . $this->defaultValue . '\')',
76+
default => var_export($this->defaultValue, true),
77+
};
78+
79+
$typeCheck = match ($phpType) {
80+
'null' => 'is_null($value)',
81+
'string' => 'is_string($value)',
82+
'int' => 'is_int($value)',
83+
'float' => 'is_float($value)',
84+
'bool' => 'is_bool($value)',
85+
'array' => 'is_array($value)',
86+
default => '$value instanceof ' . $phpType,
87+
};
88+
89+
if ($annotationType) {
90+
$propertyAccessor = <<<EOL
91+
92+
/**
93+
* @return ?$annotationType;
94+
*/
95+
public function $methodName(): $returnType
96+
{
97+
\$value = \$this->node->getProperty('$this->propertyName');
98+
if ($typeCheck) {
99+
return \$value;
100+
}
101+
return $defaultReturn;
102+
}
103+
104+
EOL;
105+
} else {
106+
$propertyAccessor = <<<EOL
107+
108+
public function $methodName(): $returnType
109+
{
110+
\$value = \$this->node->getProperty('$this->propertyName');
111+
if ($typeCheck) {
112+
return \$value;
113+
}
114+
return $defaultReturn;
115+
}
116+
117+
EOL;
118+
}
119+
120+
return $propertyAccessor;
121+
}
122+
123+
124+
public function toPhpInterfaceMethodString(): string
125+
{
126+
$propertyIsInternal = str_starts_with($this->propertyName, '_');
127+
if ($propertyIsInternal) {
128+
$methodName = 'getInternal' . UnicodeFunctions::ucfirst(substr($this->propertyName, 1));
129+
} else {
130+
$methodName = 'get' . UnicodeFunctions::ucfirst($this->propertyName);
131+
}
132+
133+
$annotationType = $this->getAnnotationType();
134+
$phpType = $this->getPhpType();
135+
$returnType = ($this->defaultValue === null) ? '?' . $phpType : $phpType;
136+
137+
138+
if ($annotationType) {
139+
$propertyAccessor = <<<EOL
140+
141+
/**
142+
* @return ?$annotationType;
143+
*/
144+
public function $methodName(): $returnType;
145+
146+
EOL;
147+
} else {
148+
$propertyAccessor = <<<EOL
149+
150+
public function $methodName(): $returnType;
151+
152+
EOL;
153+
}
154+
155+
return $propertyAccessor;
156+
}
18157
}

Classes/Domain/NodePropertySpecificationCollection.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PackageFactory\NodeTypeObjects\Domain;
66

7+
use Neos\ContentRepository\Core\NodeType\NodeType;
78
use Neos\Flow\Annotations as Flow;
89

910
/**
@@ -30,4 +31,20 @@ public function getIterator(): \Generator
3031
{
3132
yield from $this->items;
3233
}
34+
35+
public static function createFromNodeType(NodeType $nodeType): NodePropertySpecificationCollection
36+
{
37+
/**
38+
* @var NodePropertySpecification[] $propertySpecifications
39+
*/
40+
$propertySpecifications = [];
41+
foreach ($nodeType->getProperties() as $propertyName => $propertyConfiguration) {
42+
$propertySpecifications[] = new NodePropertySpecification(
43+
$propertyName,
44+
$nodeType->getPropertyType($propertyName),
45+
$propertyConfiguration['defaultValue'] ?? null
46+
);
47+
}
48+
return new NodePropertySpecificationCollection(...$propertySpecifications);
49+
}
3350
}

Classes/Domain/NodeTypeObjectFile.php

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)