Skip to content

Commit bb0d83e

Browse files
committed
FEATURE: Add migrations-transformations to allow to modify existing node properties
To convert existing properties to the new ImageSourceProxy and ImageSourceProxyCollection you can use the included transformation and configure your own content migrations. ```yaml up: comments: 'Convert Images and Asset[] to ImageSourceProxy and ImageSourceProxyCollection' migration: - filters: - type: 'NodeType' settings: nodeType: 'Vendor.Site:Node' withSubTypes: true transformations: - type: '\Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations\ImageToImageSourceProxy' settings: sourceProperty: 'image' targetProperty: 'image' altProperty: 'imageAlt' titleProperty: 'imageTitle' - type: '\Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations\AssetsToImageSourceProxyCollection' settings: sourceProperty: 'imageList' targetProperty: 'imageList' down: comments: 'Convert ImageSourceProxy and ImageSourceProxyCollection back to Images' migration: - filters: - type: 'NodeType' settings: nodeType: 'Vendor.Site:Node' withSubTypes: true transformations: - type: '\Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations\ImageSourceProxyToImage' settings: sourceProperty: 'image' targetProperty: 'image' altProperty: 'imageAlt' titleProperty: 'imageTitle' - type: '\Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations\ImageSourceProxyCollectionToAssets' settings: sourceProperty: 'imageList' targetProperty: 'imageList' ```
1 parent 751b7f6 commit bb0d83e

20 files changed

+960
-17
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ jobs:
4343
run: composer require neos/neos ^${{ matrix.neos-versions }} --no-progress --no-interaction
4444

4545
- name: Run Tests
46-
run: composer test
46+
if: ${{! startsWith(matrix.neos-versions, '9') }}
47+
run: composer test8
48+
49+
- name: Run Tests
50+
if: ${{startsWith(matrix.neos-versions, '9') }}
51+
run: composer test9

Classes/ImageAssetProxy.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,52 @@
55
namespace Sitegeist\Kaleidoscope\ValueObjects;
66

77
use Neos\Flow\Annotations as Flow;
8+
use Neos\Media\Domain\Model\AssetInterface;
9+
use Neos\Media\Domain\Model\Image;
10+
use Neos\Media\Domain\Model\ImageVariant;
811

12+
/**
13+
* @phpstan-type assetProxyShapeV8 array{__identifier: string, __flow_object_type: class-string}
14+
* @phpstan-type assetProxyShapeV9 array{identifier: string, classname: class-string}
15+
* @phpstan-type assetProxyShape assetProxyShapeV8|assetProxyShapeV9
16+
*/
917
#[Flow\Proxy(false)]
1018
final class ImageAssetProxy implements \JsonSerializable
1119
{
20+
/**
21+
* @param class-string $classname
22+
*/
1223
public function __construct(
1324
public readonly string $identifier,
1425
public readonly string $classname
1526
) {
1627
}
1728

29+
/**
30+
* @param assetProxyShape $data
31+
* @return self
32+
*/
1833
public static function fromArray(array $data): self
1934
{
2035
return new self(
36+
/** @phpstan-ignore offsetAccess.notFound*/
2137
$data['__identifier'] ?? $data['identifier'],
38+
/** @phpstan-ignore offsetAccess.notFound*/
2239
$data['__flow_object_type'] ?? $data['classname'],
2340
);
2441
}
2542

43+
public static function fromAsset(Image|ImageVariant $asset): self
44+
{
45+
return new self(
46+
$asset->getIdentifier(),
47+
get_class($asset),
48+
);
49+
}
50+
51+
/**
52+
* @return assetProxyShape
53+
*/
2654
public function jsonSerialize(): array
2755
{
2856
return [

Classes/ImageSourceProxy.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
use Neos\Flow\Annotations as Flow;
88

9+
/**
10+
* @phpstan-import-type assetProxyShape from ImageAssetProxy
11+
* @phpstan-type imagesourceProxyShape array{asset:assetProxyShape, alt?:string, title?: string}
12+
*/
913
#[Flow\Proxy(false)]
1014
final class ImageSourceProxy implements \JsonSerializable
1115
{
@@ -16,6 +20,10 @@ public function __construct(
1620
) {
1721
}
1822

23+
/**
24+
* @param imagesourceProxyShape|array{} $data
25+
* @return self|null
26+
*/
1927
public static function fromArray(array $data): ?self
2028
{
2129
// empty arrays are effectively null values and should be persisted as such
@@ -29,6 +37,9 @@ public static function fromArray(array $data): ?self
2937
);
3038
}
3139

40+
/**
41+
* @return imagesourceProxyShape
42+
*/
3243
public function jsonSerialize(): array
3344
{
3445
return [

Classes/ImageSourceProxyCollection.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
use Neos\Flow\Annotations as Flow;
88

9+
/**
10+
* @phpstan-import-type imagesourceProxyShape from ImageSourceProxy
11+
* @phpstan-type imagesourceProxycollectionShape imagesourceProxyShape[]
12+
* @implements \IteratorAggregate<int, ImageSourceProxy>
13+
*/
914
#[Flow\Proxy(false)]
1015
class ImageSourceProxyCollection implements \IteratorAggregate, \Countable, \JsonSerializable
1116
{
@@ -33,6 +38,10 @@ public function count(): int
3338
return count($this->items);
3439
}
3540

41+
/**
42+
* @param imagesourceProxycollectionShape $data
43+
* @return self
44+
*/
3645
public static function fromArray(array $data): self
3746
{
3847
$items = array_map(
@@ -42,6 +51,9 @@ public static function fromArray(array $data): self
4251
return new self(...array_filter($items));
4352
}
4453

54+
/**
55+
* @return imagesourceProxycollectionShape
56+
*/
4557
public function jsonSerialize(): array
4658
{
4759
return array_map(
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations;
6+
7+
use Neos\ContentRepository\Domain\Model\NodeData;
8+
use Neos\ContentRepository\Migration\Transformations\AbstractTransformation;
9+
use Neos\Media\Domain\Model\AssetInterface;
10+
use Neos\Media\Domain\Model\Image;
11+
use Neos\Media\Domain\Model\ImageVariant;
12+
use Sitegeist\Kaleidoscope\ValueObjects\ImageAssetProxy;
13+
use Sitegeist\Kaleidoscope\ValueObjects\ImageSourceProxy;
14+
use Sitegeist\Kaleidoscope\ValueObjects\ImageSourceProxyCollection;
15+
16+
class AssetsToImageSourceProxyCollection extends AbstractTransformation
17+
{
18+
/**
19+
* @var string
20+
*/
21+
protected $sourceProperty;
22+
23+
/**
24+
* @var string
25+
*/
26+
protected $targetProperty;
27+
28+
public function setSourceProperty(string $sourceProperty): void
29+
{
30+
$this->sourceProperty = $sourceProperty;
31+
}
32+
33+
public function setTargetProperty(string $targetProperty): void
34+
{
35+
$this->targetProperty = $targetProperty;
36+
}
37+
38+
/**
39+
* If the given node has the property this transformation should work on, this
40+
* returns true.
41+
*/
42+
public function isTransformable(NodeData $node): bool
43+
{
44+
if (
45+
$node->hasProperty($this->sourceProperty)
46+
&& $node->hasProperty($this->targetProperty)
47+
) {
48+
$sourceValue = $node->getProperty($this->sourceProperty);
49+
if (is_array($sourceValue)) {
50+
foreach ($sourceValue as $sourceItem) {
51+
if (!$sourceItem instanceof AssetInterface) {
52+
return false;
53+
}
54+
}
55+
return true;
56+
}
57+
}
58+
return false;
59+
}
60+
61+
62+
/**
63+
* from: Asset[]
64+
* to: Sitegeist\\Kaleidoscope\\ValueObjects\\ImageSourceProxyCollection
65+
**/
66+
public function execute(NodeData $node): NodeData
67+
{
68+
/**
69+
* @var array<Image|ImageVariant> $sourceValue
70+
*/
71+
$sourceValue = $node->getProperty($this->sourceProperty);
72+
$targetValue = new ImageSourceProxyCollection(
73+
...array_map(
74+
fn(Image|ImageVariant $asset) => new ImageSourceProxy(ImageAssetProxy::fromAsset($asset), "", ""),
75+
$sourceValue
76+
)
77+
);
78+
$node->setProperty($this->targetProperty, $targetValue);
79+
return $node;
80+
}
81+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations;
6+
7+
use Neos\ContentRepository\Domain\Model\NodeData;
8+
use Neos\ContentRepository\Migration\Transformations\AbstractTransformation;
9+
use Neos\Flow\Annotations as Flow;
10+
use Sitegeist\Kaleidoscope\ValueObjects\Factory\ImageAssetFactory;
11+
use Sitegeist\Kaleidoscope\ValueObjects\ImageSourceProxy;
12+
use Sitegeist\Kaleidoscope\ValueObjects\ImageSourceProxyCollection;
13+
14+
class ImageSourceProxyCollectionToAssets extends AbstractTransformation
15+
{
16+
/**
17+
* @var string
18+
*/
19+
protected $sourceProperty;
20+
21+
/**
22+
* @var string
23+
*/
24+
protected $targetProperty;
25+
26+
#[Flow\Inject]
27+
protected ImageAssetFactory $imageAssetFactory;
28+
29+
public function setSourceProperty(string $sourceProperty): void
30+
{
31+
$this->sourceProperty = $sourceProperty;
32+
}
33+
34+
public function setTargetProperty(string $targetProperty): void
35+
{
36+
$this->targetProperty = $targetProperty;
37+
}
38+
39+
/**
40+
* If the given node has the property this transformation should work on, this
41+
* returns true.
42+
*/
43+
public function isTransformable(NodeData $node): bool
44+
{
45+
if (
46+
$node->hasProperty($this->sourceProperty)
47+
&& $node->hasProperty($this->targetProperty)
48+
) {
49+
$sourceValue = $node->getProperty($this->sourceProperty);
50+
if ($sourceValue instanceof ImageSourceProxyCollection) {
51+
return true;
52+
}
53+
}
54+
return false;
55+
}
56+
57+
/**
58+
* from: Sitegeist\\Kaleidoscope\\ValueObjects\\ImageSourceProxyCollection
59+
* to: Asset[]
60+
*/
61+
public function execute(NodeData $node): NodeData
62+
{
63+
/**
64+
* @var ImageSourceProxyCollection $sourceValue
65+
*/
66+
$sourceValue = $node->getProperty($this->sourceProperty);
67+
$targetValue = array_map(
68+
function (ImageSourceProxy $item) {
69+
return $this->imageAssetFactory->tryCreateFromProxy($item->asset);
70+
},
71+
$sourceValue->items
72+
);
73+
$node->setProperty($this->targetProperty, $targetValue);
74+
return $node;
75+
}
76+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\Kaleidoscope\ValueObjects\Migration\Transformations;
6+
7+
use Imagine\Gd\Image;
8+
use Neos\ContentRepository\Domain\Model\NodeData;
9+
use Neos\ContentRepository\Migration\Transformations\AbstractTransformation;
10+
use Neos\Media\Domain\Model\AssetInterface;
11+
use Sitegeist\Kaleidoscope\ValueObjects\Factory\ImageAssetFactory;
12+
use Sitegeist\Kaleidoscope\ValueObjects\ImageSourceProxy;
13+
use Neos\Flow\Annotations as Flow;
14+
15+
class ImageSourceProxyToImage extends AbstractTransformation
16+
{
17+
/**
18+
* @var string
19+
*/
20+
protected $sourceProperty;
21+
22+
/**
23+
* @var string
24+
*/
25+
protected $targetProperty;
26+
27+
/**
28+
* @var null|string
29+
*/
30+
protected $titleProperty = null;
31+
32+
/**
33+
* @var null|string
34+
*/
35+
protected $altProperty = null;
36+
37+
#[Flow\Inject]
38+
protected ImageAssetFactory $imageAssetFactory;
39+
40+
public function setSourceProperty(string $sourceProperty): void
41+
{
42+
$this->sourceProperty = $sourceProperty;
43+
}
44+
45+
public function setTargetProperty(string $targetProperty): void
46+
{
47+
$this->targetProperty = $targetProperty;
48+
}
49+
50+
public function setAltProperty(?string $altProperty): void
51+
{
52+
$this->altProperty = $altProperty;
53+
}
54+
55+
public function setTitleProperty(?string $titleProperty): void
56+
{
57+
$this->titleProperty = $titleProperty;
58+
}
59+
60+
/**
61+
* If the given node has the property this transformation should work on, this
62+
* returns true.
63+
*/
64+
public function isTransformable(NodeData $node): bool
65+
{
66+
if (
67+
$node->hasProperty($this->sourceProperty)
68+
&& $node->hasProperty($this->targetProperty)
69+
&& ($this->altProperty === null || $node->hasProperty($this->altProperty))
70+
&& ($this->titleProperty === null || $node->hasProperty($this->titleProperty))
71+
) {
72+
$sourceValue = $node->getProperty($this->sourceProperty);
73+
if (
74+
$sourceValue instanceof ImageSourceProxy
75+
) {
76+
return true;
77+
}
78+
}
79+
return false;
80+
}
81+
82+
/**
83+
* Change the property on the given node.
84+
*
85+
* from: Sitegeist\\Kaleidoscope\\ValueObjects\\ImageSourceProxy
86+
* to: Neos\\Media\\Domain\\Model\\Image
87+
*/
88+
public function execute(NodeData $node): NodeData
89+
{
90+
/**
91+
* @var ImageSourceProxy $sourceValue
92+
*/
93+
$sourceValue = $node->getProperty($this->sourceProperty);
94+
if ($this->altProperty) {
95+
$node->setProperty($this->altProperty, $sourceValue->alt);
96+
}
97+
if ($this->titleProperty) {
98+
$node->setProperty($this->titleProperty, $sourceValue->title);
99+
}
100+
$targetValue = $this->imageAssetFactory->tryCreateFromProxy($sourceValue->asset);
101+
$node->setProperty($this->targetProperty, $targetValue);
102+
return $node;
103+
}
104+
}

0 commit comments

Comments
 (0)