From 671b2ebcd2f9ba49b655d633db5713b265e6917d Mon Sep 17 00:00:00 2001 From: Adrian Cerdeira Date: Thu, 10 Jul 2025 10:42:24 +0200 Subject: [PATCH 1/8] TASK: Neos 9 Compatibility with rector --- Classes/Migration/Transformation/PropertyValueToLowercase.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Classes/Migration/Transformation/PropertyValueToLowercase.php b/Classes/Migration/Transformation/PropertyValueToLowercase.php index dde1f30..58f823a 100644 --- a/Classes/Migration/Transformation/PropertyValueToLowercase.php +++ b/Classes/Migration/Transformation/PropertyValueToLowercase.php @@ -7,7 +7,8 @@ use Neos\ContentRepository\Domain\Model\NodeData; use Neos\ContentRepository\Migration\Transformations\AbstractTransformation; -class PropertyValueToLowercase extends AbstractTransformation +// TODO 9.0 migration: You need to convert your AbstractTransformation to an implementation of Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface +class PropertyValueToLowercase { private string $propertyName; From cb633ddee088259b278dc5eadd7eb5e6dd932a0d Mon Sep 17 00:00:00 2001 From: Adrian Cerdeira Date: Thu, 17 Jul 2025 07:32:22 +0200 Subject: [PATCH 2/8] feat: resolve todos and create custom transformation (who cannot be used at the moment) # Conflicts: # README.md --- .../PropertyValueToLowercase.php | 79 ++++++++++++------- .../Version20250124153030.yaml | 29 +++---- README.md | 2 +- composer.json | 2 +- 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/Classes/Migration/Transformation/PropertyValueToLowercase.php b/Classes/Migration/Transformation/PropertyValueToLowercase.php index 58f823a..dfa6445 100644 --- a/Classes/Migration/Transformation/PropertyValueToLowercase.php +++ b/Classes/Migration/Transformation/PropertyValueToLowercase.php @@ -4,39 +4,58 @@ namespace Flowpack\SeoRouting\Migration\Transformation; -use Neos\ContentRepository\Domain\Model\NodeData; -use Neos\ContentRepository\Migration\Transformations\AbstractTransformation; - -// TODO 9.0 migration: You need to convert your AbstractTransformation to an implementation of Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface -class PropertyValueToLowercase +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; +use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\NodeMigration\Transformation\NodeBasedTransformationInterface; +use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; +use Neos\ContentRepository\NodeMigration\Transformation\GlobalTransformationInterface; +use Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface; +use Neos\ContentRepository\NodeMigration\Transformation\TransformationStep; + +/** + * Transforms a specified property value of a node to lowercase. + */ +class PropertyValueToLowercase implements TransformationFactoryInterface { - private string $propertyName; - - public function setProperty(string $propertyName): void - { - $this->propertyName = $propertyName; - } - - /** - * @inheritDoc - */ - public function isTransformable(NodeData $node) - { - return $node->hasProperty($this->propertyName); - } - /** - * @inheritDoc + * @param array $settings */ - public function execute(NodeData $node) + public function build(array $settings, ContentRepository $contentRepository): GlobalTransformationInterface|NodeBasedTransformationInterface { - $currentPropertyValue = $node->getProperty($this->propertyName); - if (! is_string($currentPropertyValue)) { - return $node; - } - $newPropertyValue = strtolower($currentPropertyValue); - $node->setProperty($this->propertyName, $newPropertyValue); - - return $node; + return new class( + $settings['property'] + ) implements NodeBasedTransformationInterface { + + private string $propertyName; + + public function __construct(string $propertyName) + { + $this->propertyName = $propertyName; + } + + public function execute(Node $node, DimensionSpacePoint $coveredDimensionSpacePoints, WorkspaceName $workspaceNameForWriting): TransformationStep + { + $currentProperty = $node->getProperty($this->propertyName); + + if ($currentProperty !== null && is_string($currentProperty)) { + $value = strtolower($currentProperty); + return TransformationStep::fromCommand( + SetNodeProperties::create( + $workspaceNameForWriting, + $node->aggregateId, + $node->originDimensionSpacePoint, + PropertyValuesToWrite::fromArray([ + $this->propertyName => $value, + ]) + ) + ); + } + + return TransformationStep::createEmpty(); + } + }; } } diff --git a/Migrations/ContentRepository/Version20250124153030.yaml b/Migrations/ContentRepository/Version20250124153030.yaml index a17f8d7..991e8c2 100644 --- a/Migrations/ContentRepository/Version20250124153030.yaml +++ b/Migrations/ContentRepository/Version20250124153030.yaml @@ -1,16 +1,13 @@ -up: - comments: 'Transforms all uriPathSegment values to lowercase' - warnings: 'As this migration removes the distinction between uppercase and lowercase it might not be cleanly undone by the down migration.' - migration: - - filters: - - type: 'NodeType' - settings: - nodeType: 'Neos.Neos:Document' - withSubTypes: TRUE - transformations: - - type: 'Flowpack\SeoRouting\Migration\Transformation\PropertyValueToLowercase' - settings: - property: 'uriPathSegment' - -down: - comments: 'No down migration available' +comments: "Transforms all uriPathSegment values to lowercase" +warnings: "As this migration removes the distinction between uppercase and lowercase it might not be cleanly undone by the down migration." +migration: + - filters: + - type: "NodeType" + settings: + nodeType: "Neos.Neos:Document" + withSubTypes: TRUE + # Custom transformation not possible in Neos < 9.0: https://neos-project.slack.com/archives/C04V4C6B0/p1752678184783919 + # transformations: + # - type: 'Flowpack\SeoRouting\Migration\Transformation\PropertyValueToLowercase' + # settings: + # property: "uriPathSegment" diff --git a/README.md b/README.md index 69285ff..858802b 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ composer require flowpack/seo-routing If you want to use the *toLowerCase* feature you should execute the migration that comes with this package: ``` -./flow node:migrate 20250124153030 --confirmation true +./flow nodemigration:execute 20250124153030 --force ``` > [!WARNING] diff --git a/composer.json b/composer.json index cbf61e9..10822a0 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "require": { "guzzlehttp/psr7": "^2.0", "php": "^8.1", - "neos/neos": "^8.3|^9.0" + "neos/neos": "^9.0" }, "require-dev": { "phpstan/phpstan": "^2.1", From 4cefcae5407ffae0cc27650cc6c23cd07f569553 Mon Sep 17 00:00:00 2001 From: Adrian Cerdeira Date: Thu, 17 Jul 2025 09:24:37 +0200 Subject: [PATCH 3/8] fix: property value migration and add into settings custom transformation --- .../Transformation/PropertyValueToLowercase.php | 6 +++--- Configuration/Settings.yaml | 5 +++++ Migrations/ContentRepository/Version20250124153030.yaml | 9 ++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Classes/Migration/Transformation/PropertyValueToLowercase.php b/Classes/Migration/Transformation/PropertyValueToLowercase.php index dfa6445..5a329a4 100644 --- a/Classes/Migration/Transformation/PropertyValueToLowercase.php +++ b/Classes/Migration/Transformation/PropertyValueToLowercase.php @@ -4,7 +4,7 @@ namespace Flowpack\SeoRouting\Migration\Transformation; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\NodeMigration\Transformation\NodeBasedTransformationInterface; @@ -36,12 +36,12 @@ public function __construct(string $propertyName) $this->propertyName = $propertyName; } - public function execute(Node $node, DimensionSpacePoint $coveredDimensionSpacePoints, WorkspaceName $workspaceNameForWriting): TransformationStep - { + public function execute(Node $node, DimensionSpacePointSet $coveredDimensionSpacePoints, WorkspaceName $workspaceNameForWriting): TransformationStep { $currentProperty = $node->getProperty($this->propertyName); if ($currentProperty !== null && is_string($currentProperty)) { $value = strtolower($currentProperty); + return TransformationStep::fromCommand( SetNodeProperties::create( $workspaceNameForWriting, diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 77ff9d2..7526d74 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -15,3 +15,8 @@ Neos: middlewares: 'after routing': middleware: 'Flowpack\SeoRouting\RoutingMiddleware' + + ContentRepositoryRegistry: + nodeMigration: + transformationFactories: + PropertyValueToLowercase: Flowpack\SeoRouting\Migration\Transformation\PropertyValueToLowercase diff --git a/Migrations/ContentRepository/Version20250124153030.yaml b/Migrations/ContentRepository/Version20250124153030.yaml index 991e8c2..8792187 100644 --- a/Migrations/ContentRepository/Version20250124153030.yaml +++ b/Migrations/ContentRepository/Version20250124153030.yaml @@ -6,8 +6,7 @@ migration: settings: nodeType: "Neos.Neos:Document" withSubTypes: TRUE - # Custom transformation not possible in Neos < 9.0: https://neos-project.slack.com/archives/C04V4C6B0/p1752678184783919 - # transformations: - # - type: 'Flowpack\SeoRouting\Migration\Transformation\PropertyValueToLowercase' - # settings: - # property: "uriPathSegment" + transformations: + - type: 'PropertyValueToLowercase' + settings: + property: "uriPathSegment" From 63565ca859278d44297a11ba8fb6a9ebeee98899 Mon Sep 17 00:00:00 2001 From: Timon Heuser Date: Thu, 16 Apr 2026 11:35:36 +0200 Subject: [PATCH 4/8] fix composer deps --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 10822a0..917c754 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "neos/neos": "^9.0" }, "require-dev": { + "neos/contentrepositoryregistry-doctrinedbalclient": "*", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpstan/extension-installer": "^1.4", From 7a439d4f4052e16898924f595a0269354e8dc264 Mon Sep 17 00:00:00 2001 From: Timon Heuser Date: Thu, 16 Apr 2026 11:40:18 +0200 Subject: [PATCH 5/8] fix composer deps --- composer.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 917c754..6309b0d 100644 --- a/composer.json +++ b/composer.json @@ -12,10 +12,13 @@ }, "require": { "guzzlehttp/psr7": "^2.0", - "php": "^8.1", - "neos/neos": "^9.0" + "php": "^8.1" + }, + "conflict": { + "neos/neos": "<9.0" }, "require-dev": { + "neos/neos": "^9.0", "neos/contentrepositoryregistry-doctrinedbalclient": "*", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", From 1fd83f50d1346c6675aadc8ccb86274423feed32 Mon Sep 17 00:00:00 2001 From: Timon Heuser Date: Thu, 16 Apr 2026 11:43:38 +0200 Subject: [PATCH 6/8] fix composer deps --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6309b0d..3151c50 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require-dev": { "neos/neos": "^9.0", - "neos/contentrepositoryregistry-doctrinedbalclient": "*", + "neos/contentrepositoryregistry-doctrinedbalclient": "^9.0@beta", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpstan/extension-installer": "^1.4", From 4fe610677bac097f85dc917c55487bef6fed2666 Mon Sep 17 00:00:00 2001 From: Timon Heuser Date: Thu, 16 Apr 2026 11:45:21 +0200 Subject: [PATCH 7/8] fix composer deps --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3151c50..2316a34 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require-dev": { "neos/neos": "^9.0", - "neos/contentrepositoryregistry-doctrinedbalclient": "^9.0@beta", + "neos/contentgraph-doctrinedbaladapter": "^9.0", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", "phpstan/extension-installer": "^1.4", From de255a509cc858876bbd221d8598f6551ce66731 Mon Sep 17 00:00:00 2001 From: Timon Heuser Date: Thu, 16 Apr 2026 11:49:00 +0200 Subject: [PATCH 8/8] fix phpstan --- .../PropertyValueToLowercase.php | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/Classes/Migration/Transformation/PropertyValueToLowercase.php b/Classes/Migration/Transformation/PropertyValueToLowercase.php index 5a329a4..34c3547 100644 --- a/Classes/Migration/Transformation/PropertyValueToLowercase.php +++ b/Classes/Migration/Transformation/PropertyValueToLowercase.php @@ -4,14 +4,14 @@ namespace Flowpack\SeoRouting\Migration\Transformation; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\NodeMigration\Transformation\NodeBasedTransformationInterface; use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; +use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\NodeMigration\Transformation\GlobalTransformationInterface; +use Neos\ContentRepository\NodeMigration\Transformation\NodeBasedTransformationInterface; use Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface; use Neos\ContentRepository\NodeMigration\Transformation\TransformationStep; @@ -21,10 +21,14 @@ class PropertyValueToLowercase implements TransformationFactoryInterface { /** - * @param array $settings + * @inheritDoc */ - public function build(array $settings, ContentRepository $contentRepository): GlobalTransformationInterface|NodeBasedTransformationInterface + public function build( + array $settings, + ContentRepository $contentRepository + ): GlobalTransformationInterface|NodeBasedTransformationInterface { + /** @var array{property: string} $settings */ return new class( $settings['property'] ) implements NodeBasedTransformationInterface { @@ -36,25 +40,30 @@ public function __construct(string $propertyName) $this->propertyName = $propertyName; } - public function execute(Node $node, DimensionSpacePointSet $coveredDimensionSpacePoints, WorkspaceName $workspaceNameForWriting): TransformationStep { + public function execute( + Node $node, + DimensionSpacePointSet $coveredDimensionSpacePoints, + WorkspaceName $workspaceNameForWriting + ): TransformationStep + { $currentProperty = $node->getProperty($this->propertyName); - if ($currentProperty !== null && is_string($currentProperty)) { - $value = strtolower($currentProperty); - - return TransformationStep::fromCommand( - SetNodeProperties::create( - $workspaceNameForWriting, - $node->aggregateId, - $node->originDimensionSpacePoint, - PropertyValuesToWrite::fromArray([ - $this->propertyName => $value, - ]) - ) - ); + if (!is_string($currentProperty)) { + return TransformationStep::createEmpty(); } - return TransformationStep::createEmpty(); + $value = strtolower($currentProperty); + + return TransformationStep::fromCommand( + SetNodeProperties::create( + $workspaceNameForWriting, + $node->aggregateId, + $node->originDimensionSpacePoint, + PropertyValuesToWrite::fromArray([ + $this->propertyName => $value, + ]) + ) + ); } }; }