Skip to content

Commit 77eebbd

Browse files
authored
[Asset Metadata] Implement adapter approach (#951)
* Add adapter approach for asset metadata * Apply php-cs-fixer changes * remove empty block
1 parent fd38853 commit 77eebbd

18 files changed

Lines changed: 489 additions & 55 deletions

config/metadata.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ services:
1616
Pimcore\Bundle\StudioBackendBundle\Metadata\Service\MetadataServiceInterface:
1717
class: Pimcore\Bundle\StudioBackendBundle\Metadata\Service\MetadataService
1818

19+
Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataAdapterLoaderInterface:
20+
class: Pimcore\Bundle\StudioBackendBundle\Metadata\Service\Loader\TaggedIteratorMetadataAdapter
21+
22+
Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataResolverServiceInterface:
23+
class: Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataResolverServiceService
24+
25+
Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataAdapterServiceInterface:
26+
class: Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataAdapterService
27+
arguments:
28+
$loader: '@pimcore.implementation_loader.asset.metadata.data'
1929

2030
# Hydrator
2131
Pimcore\Bundle\StudioBackendBundle\Metadata\Hydrator\MetadataHydratorInterface:

config/pimcore/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ pimcore_studio_backend:
9999
document: ['content', 'seo', 'warning', 'notice']
100100
data-object: ['content', 'seo', 'warning', 'notice']
101101

102+
asset_metadata_adapter_mapping: []
103+
102104
data_object_data_adapter_mapping:
103105
Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter\AdvancedManyToManyRelationAdapter:
104106
- "advancedManyToManyRelation"

src/DataObject/Service/DataAdapterLoaderInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
interface DataAdapterLoaderInterface
2626
{
27-
public const ADAPTER_TAG = 'pimcore.studio_backend.data_adapter';
27+
public const string ADAPTER_TAG = 'pimcore.studio_backend.data_adapter';
2828

2929
/**
3030
* @throws InvalidArgumentException

src/DependencyInjection/Configuration.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public function getConfigTreeBuilder(): TreeBuilder
7272
$this->addGridConfiguration($rootNode);
7373
$this->addSearchGridConfiguration($rootNode);
7474
$this->addNoteTypes($rootNode);
75+
$this->addAssetMetadataAdapterMapping($rootNode);
7576
$this->addDataObjectAdapterMapping($rootNode);
7677
$this->addUserNode($rootNode);
7778
$this->addServerNode($rootNode);
@@ -370,6 +371,18 @@ private function addDataObjectAdapterMapping(ArrayNodeDefinition $node): void
370371
->end();
371372
}
372373

374+
private function addAssetMetadataAdapterMapping(ArrayNodeDefinition $node): void
375+
{
376+
$node->children()
377+
->arrayNode('asset_metadata_adapter_mapping')
378+
->useAttributeAsKey('class')
379+
->arrayPrototype()
380+
->scalarPrototype()
381+
->end()
382+
->end()
383+
->end();
384+
}
385+
373386
private function addUserNode(ArrayNodeDefinition $node): void
374387
{
375388
$node->children()

src/DependencyInjection/PimcoreStudioBackendExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Pimcore\Bundle\StudioBackendBundle\Grid\Column\Collector\DataObject\AdvancedColumnCollector;
3131
use Pimcore\Bundle\StudioBackendBundle\Grid\Service\ConfigurationServiceInterface;
3232
use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\HubServiceInterface;
33+
use Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataAdapterServiceInterface as MetadataAdapterServiceInterface;
3334
use Pimcore\Bundle\StudioBackendBundle\Note\Service\NoteServiceInterface;
3435
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Service\OpenApiServiceInterface;
3536
use Pimcore\Bundle\StudioBackendBundle\Perspective\Repository\ElementTreeWidgetConfigRepository;
@@ -162,6 +163,9 @@ public function load(array $configs, ContainerBuilder $container): void
162163
'$perspectiveConfigurations' => $config[Configuration::PERSPECTIVES_NODE],
163164
'$storageConfig' => $config['config_location'][Configuration::PERSPECTIVES_NODE],
164165
]);
166+
167+
$definition = $container->getDefinition(MetadataAdapterServiceInterface::class);
168+
$definition->setArgument('$studioAdapters', $config['asset_metadata_adapter_mapping']);
165169
}
166170

167171
/**
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Metadata\Data;
18+
19+
use Pimcore\Model\UserInterface;
20+
21+
interface DataDenormalizerInterface
22+
{
23+
public function denormalize(
24+
mixed $value,
25+
string $type,
26+
UserInterface $user
27+
): mixed;
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Metadata\Data;
18+
19+
interface DataNormalizerInterface
20+
{
21+
public function normalize(
22+
mixed $value,
23+
string $type
24+
): mixed;
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Pimcore
6+
*
7+
* This source file is available under two different licenses:
8+
* - GNU General Public License version 3 (GPLv3)
9+
* - Pimcore Commercial License (PCL)
10+
* Full copyright and license information is available in
11+
* LICENSE.md which is distributed with this source code.
12+
*
13+
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
14+
* @license http://www.pimcore.org/license GPLv3 and PCL
15+
*/
16+
17+
namespace Pimcore\Bundle\StudioBackendBundle\Metadata\Data;
18+
19+
interface MetaDataAdapterInterface
20+
{
21+
}

src/Metadata/Hydrator/MetadataHydrator.php

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@
1818

1919
use Pimcore\Bundle\StudioBackendBundle\Metadata\Schema\CustomMetadata;
2020
use Pimcore\Bundle\StudioBackendBundle\Metadata\Schema\PredefinedMetadata;
21+
use Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataResolverServiceInterface;
2122
use Pimcore\Bundle\StudioBackendBundle\Resolver\Element\ReferenceResolverInterface;
22-
use Pimcore\Model\Element\ElementInterface;
2323
use Pimcore\Model\Metadata\Predefined;
2424

2525
/**
2626
* @internal
2727
*/
2828
final readonly class MetadataHydrator implements MetadataHydratorInterface
2929
{
30-
public function __construct(private ReferenceResolverInterface $referenceResolver)
31-
{
30+
public function __construct(
31+
private DataResolverServiceInterface $dataResolverService,
32+
private ReferenceResolverInterface $referenceResolver
33+
) {
3234
}
3335

3436
public function hydrate(array $customMetadata): CustomMetadata
@@ -37,10 +39,7 @@ public function hydrate(array $customMetadata): CustomMetadata
3739
$customMetadata['name'],
3840
$customMetadata['language'],
3941
$customMetadata['type'],
40-
$this->resolveData(
41-
$customMetadata['data'],
42-
$customMetadata['type']
43-
)
42+
$this->dataResolverService->normalizeData($customMetadata),
4443
);
4544
}
4645

@@ -65,19 +64,6 @@ public function hydratePredefined(Predefined $predefined): PredefinedMetadata
6564
);
6665
}
6766

68-
private function resolveData(mixed $data, string $type): mixed
69-
{
70-
return match (true) {
71-
$data instanceof ElementInterface => $this->referenceResolver->resolve($data),
72-
$type === 'manyToManyRelation' => array_map(
73-
fn (ElementInterface $element): array => $this->referenceResolver->resolve($element),
74-
$data
75-
),
76-
$type === 'checkbox' => (bool)$data,
77-
default => $data,
78-
};
79-
}
80-
8167
private function resolveDefinitionData(mixed $data, string $type): mixed
8268
{
8369
if (!$data) {

src/Metadata/Patcher/Adapter/CustomMetadataAdapter.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException;
2020
use Pimcore\Bundle\StudioBackendBundle\Metadata\Repository\MetadataRepositoryInterface;
21+
use Pimcore\Bundle\StudioBackendBundle\Metadata\Service\DataResolverServiceInterface;
2122
use Pimcore\Bundle\StudioBackendBundle\Metadata\Service\MetadataServiceInterface;
2223
use Pimcore\Bundle\StudioBackendBundle\Patcher\Adapter\PatchAdapterInterface;
2324
use Pimcore\Bundle\StudioBackendBundle\Patcher\Service\Loader\TaggedIteratorAdapter;
@@ -28,22 +29,25 @@
2829
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
2930
use function array_key_exists;
3031
use function in_array;
32+
use function sprintf;
3133

3234
/**
3335
* @internal
3436
*/
3537
#[AutoconfigureTag(TaggedIteratorAdapter::ADAPTER_TAG)]
3638
final class CustomMetadataAdapter implements PatchAdapterInterface
3739
{
38-
private const INDEX_KEY = 'metadata';
40+
private const string INDEX_KEY = 'metadata';
3941

40-
private const PATCHABLE_KEYS = [
42+
private const array PATCHABLE_KEYS = [
4143
'language',
4244
'data',
4345
];
4446

45-
public function __construct(private readonly MetadataRepositoryInterface $metadataRepository)
46-
{
47+
public function __construct(
48+
private readonly DataResolverServiceInterface $dataResolverService,
49+
private readonly MetadataRepositoryInterface $metadataRepository
50+
) {
4751
}
4852

4953
/**
@@ -67,9 +71,9 @@ public function patch(ElementInterface $element, array $data, UserInterface $use
6771
continue;
6872
}
6973

70-
foreach (self::PATCHABLE_KEYS as $patchKeys) {
71-
if (array_key_exists($patchKeys, $metadataForPatch[$index])) {
72-
$metadata[$patchKeys] = $metadataForPatch[$index][$patchKeys];
74+
foreach (self::PATCHABLE_KEYS as $patchKey) {
75+
if (array_key_exists($patchKey, $metadataForPatch[$index])) {
76+
$metadata[$patchKey] = $this->getExistingEntryValue($metadataForPatch[$index], $patchKey, $user);
7377
}
7478
}
7579
$patchedMetadata[] = $metadata;
@@ -81,7 +85,7 @@ public function patch(ElementInterface $element, array $data, UserInterface $use
8185
$patchedMetadata = [
8286
...$patchedMetadata,
8387
...array_map(
84-
fn (array $metaData) => $this->processNewMetadataEntry($metaData),
88+
fn (array $metaData) => $this->processNewMetadataEntry($metaData, $user),
8589
$metadataForPatch
8690
),
8791
];
@@ -103,10 +107,19 @@ public function supportedElementTypes(): array
103107
];
104108
}
105109

110+
private function getExistingEntryValue(array $metadata, string $key, UserInterface $user): mixed
111+
{
112+
if ($key !== 'data') {
113+
return $metadata[$key];
114+
}
115+
116+
return $this->dataResolverService->denormalizeData($metadata, $user);
117+
}
118+
106119
/**
107120
* @throws InvalidArgumentException
108121
*/
109-
private function processNewMetadataEntry(array $metadata): array
122+
private function processNewMetadataEntry(array $metadata, UserInterface $user): array
110123
{
111124
if (!isset($metadata['name'])) {
112125
throw new InvalidArgumentException('Metadata name is required');
@@ -119,14 +132,14 @@ private function processNewMetadataEntry(array $metadata): array
119132
$predefined = $this->metadataRepository->getPredefinedMetadataByName($metadata['name']);
120133

121134
if (!$predefined) {
122-
throw new InvalidArgumentException('Predefined metadata not found');
135+
throw new InvalidArgumentException(sprintf('Predefined metadata %s not found', $metadata['name']));
123136
}
124137

125138
return [
126139
'name' => $predefined->getName(),
127140
'language' => $metadata['language'] ?? '',
128141
'type' => $predefined->getType(),
129-
'data' => $metadata['data'] ?? null,
142+
'data' => $metadata['data'] ? $this->dataResolverService->denormalizeData($metadata, $user) : null,
130143
];
131144
}
132145

0 commit comments

Comments
 (0)