Skip to content

Commit

Permalink
[Data Object Editor] Add normalizer for all field types (#578)
Browse files Browse the repository at this point in the history
* feat: add normalizer for data objects

* Apply php-cs-fixer changes

---------

Co-authored-by: lukmzig <[email protected]>
  • Loading branch information
lukmzig and lukmzig authored Nov 27, 2024
1 parent 25c504c commit 5680cea
Show file tree
Hide file tree
Showing 23 changed files with 783 additions and 31 deletions.
3 changes: 3 additions & 0 deletions config/data_objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ services:
Pimcore\Bundle\StudioBackendBundle\DataObject\Service\ReplaceServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\ReplaceService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterService

Expand Down
3 changes: 0 additions & 3 deletions config/updater.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ services:
Pimcore\Bundle\StudioBackendBundle\Updater\Service\AdapterLoaderInterface:
class: Pimcore\Bundle\StudioBackendBundle\Updater\Service\Loader\TaggedIteratorAdapter

Pimcore\Bundle\StudioBackendBundle\Updater\Service\EditableAdapterLoaderInterface:
class: Pimcore\Bundle\StudioBackendBundle\Updater\Service\Loader\TaggedIteratorEditableAdapter

Pimcore\Bundle\StudioBackendBundle\Updater\Service\UpdateServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\Updater\Service\UpdateService

Expand Down
2 changes: 1 addition & 1 deletion src/Asset/Service/AssetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function getAsset(
$user = $this->securityService->getCurrentUser();
$asset = $this->assetSearchService->getAssetById($id, $user);
if ($getWorkflowAvailable) {
$asset->setHasWorkflowAvailable($this->workflowDetailsService->hasElementWorkflows(
$asset->setHasWorkflowAvailable($this->workflowDetailsService->hasElementWorkflowsById(
$id,
ElementTypes::TYPE_ASSET,
$user
Expand Down
39 changes: 38 additions & 1 deletion src/DataObject/Data/Adapter/BlockAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,30 @@
namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter;

use Exception;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidDataTypeException;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\Block;
use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\DataObject\Data\BlockElement;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
use function get_class;
use function is_array;

/**
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class BlockAdapter implements SetterDataInterface
final readonly class BlockAdapter implements SetterDataInterface, DataNormalizerInterface
{
public function __construct(
private DataAdapterServiceInterface $dataAdapterService,
private DataServiceInterface $dataService
) {
}

Expand All @@ -57,6 +63,37 @@ public function getDataForSetter(
return $this->processBlockData($element, $fieldDefinition, $blockData, $contextData);
}

public function normalize(
mixed $value,
Data $fieldDefinition
): ?array {
if (!is_array($value)) {
return null;
}

$resultItems = [];
if (!$fieldDefinition instanceof Block) {
throw new InvalidDataTypeException(Block::class, get_class($fieldDefinition));
}
$fieldDefinitions = $fieldDefinition->getFieldDefinitions();
foreach ($value as $block) {
$resultItem = [];

/** @var BlockElement $fieldValue */
foreach ($block as $key => $fieldValue) {
$blockDefinition = $fieldDefinitions[$key];
$resultItems[$key] = $this->dataService->getNormalizedValue(
$fieldValue->getData(),
$blockDefinition,
);
}

$resultItems[] = $resultItem;
}

return $resultItems;
}

/**
* @throws Exception
*/
Expand Down
128 changes: 120 additions & 8 deletions src/DataObject/Data/Adapter/ClassificationStoreAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,25 @@
namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter;

use Exception;
use Pimcore\Bundle\GenericDataIndexBundle\Model\SearchIndexAdapter\MappingProperty;
use Pimcore\Bundle\StaticResolverBundle\Lib\ToolResolverInterface;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\DefinitionCacheResolverInterface;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\GroupConfigResolverInterface;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\ServiceResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\DatabaseException;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\Classificationstore as ClassificationstoreDefinition;
use Pimcore\Model\DataObject\Classificationstore;
use Pimcore\Model\DataObject\Classificationstore as ClassificationstoreModel;
use Pimcore\Model\DataObject\Classificationstore\GroupConfig;
use Pimcore\Model\DataObject\Classificationstore\KeyGroupRelation;
use Pimcore\Model\DataObject\Classificationstore\KeyGroupRelation\Listing as KeyGroupRelationListing;
use Pimcore\Model\DataObject\Concrete;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
use function in_array;
Expand All @@ -34,11 +45,15 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class ClassificationStoreAdapter implements SetterDataInterface
final readonly class ClassificationStoreAdapter implements SetterDataInterface, DataNormalizerInterface
{
public function __construct(
private DefinitionCacheResolverInterface $definitionCacheResolver,
private DataAdapterServiceInterface $dataAdapterService,
private ServiceResolverInterface $serviceResolver
private DataServiceInterface $dataService,
private GroupConfigResolverInterface $groupConfigResolver,
private ServiceResolverInterface $serviceResolver,
private ToolResolverInterface $toolResolver
) {
}

Expand Down Expand Up @@ -68,6 +83,36 @@ public function getDataForSetter(
return $container;
}

public function normalize(
mixed $value,
Data $fieldDefinition
): ?array {
if (!$value instanceof ClassificationstoreModel ||
!$fieldDefinition instanceof ClassificationstoreDefinition
) {
return null;
}

$validLanguages = $this->getValidLanguages($fieldDefinition);
$resultItems = [];

foreach ($this->getActiveGroups($value) as $groupId => $groupConfig) {
$resultItems[$groupConfig->getName()] = [];
$keys = $this->getClassificationStoreKeysFromGroup($groupConfig);
foreach ($validLanguages as $validLanguage) {
foreach ($keys as $key) {
$normalizedValue = $this->getNormalizedValue($value, $groupId, $key, $validLanguage);

if ($normalizedValue !== null) {
$resultItems[$groupConfig->getName()][$validLanguage][$key->getName()] = $normalizedValue;
}
}
}
}

return $resultItems;
}

/**
* @throws Exception
*/
Expand All @@ -89,13 +134,10 @@ private function setMapping(Classificationstore $container, array $data): void
{
$activeGroups = $data['activeGroups'];
$groupCollectionMapping = $data['groupCollectionMapping'];
$correctedMapping = [];

foreach ($groupCollectionMapping as $groupId => $collectionId) {
if (isset($activeGroups[$groupId]) && $activeGroups[$groupId]) {
$correctedMapping[$groupId] = $collectionId;
}
}
$correctedMapping = array_filter($groupCollectionMapping, static function ($groupId) use ($activeGroups) {
return isset($activeGroups[$groupId]) && $activeGroups[$groupId];
}, ARRAY_FILTER_USE_KEY);

$container->setGroupCollectionMappings($correctedMapping);
}
Expand Down Expand Up @@ -163,4 +205,74 @@ private function cleanupStoreGroups(Classificationstore $container): void
}
}
}

private function getValidLanguages(ClassificationstoreDefinition $classificationStore): array
{
$languages = [MappingProperty::NOT_LOCALIZED_KEY];
if ($classificationStore->isLocalized()) {
$languages = array_merge($languages, $this->toolResolver->getValidLanguages());
}

return $languages;
}

/**
* @return GroupConfig[]
*/
private function getActiveGroups(ClassificationstoreModel $value): array
{
$groups = [];
foreach ($value->getActiveGroups() as $groupId => $active) {
if ($active) {
$groupConfig = $this->groupConfigResolver->getById($groupId);
if ($groupConfig) {
$groups[$groupId] = $groupConfig;
}
}
}

return $groups;
}

/**
* @return KeyGroupRelation[]
*/
private function getClassificationStoreKeysFromGroup(GroupConfig $groupConfig): array
{
$listing = new KeyGroupRelationListing();
$listing->addConditionParam('groupId = ?', $groupConfig->getId());

return $listing->getList();
}

private function getNormalizedValue(
ClassificationstoreModel $classificationstore,
int $groupId,
KeyGroupRelation $key,
string $language
): mixed {
try {
$value = $classificationstore->getLocalizedKeyValue(
$groupId,
$key->getKeyId(),
$language,
true,
true
);
} catch (Exception $exception) {
throw new DatabaseException($exception->getMessage());
}

$keyConfig = $this->definitionCacheResolver->get($key->getKeyId());
if ($keyConfig === null) {
return null;
}

$fieldDefinition = $this->serviceResolver->getFieldDefinitionFromKeyConfig($keyConfig);
if ($fieldDefinition === null) {
return null;
}

return $this->dataService->getNormalizedValue($value, $fieldDefinition);
}
}
41 changes: 40 additions & 1 deletion src/DataObject/Data/Adapter/FieldCollectionsAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter;

use Exception;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\FieldCollection\DefinitionResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\Fieldcollections;
use Pimcore\Model\DataObject\Concrete;
Expand All @@ -33,10 +36,12 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class FieldCollectionsAdapter implements SetterDataInterface
final readonly class FieldCollectionsAdapter implements SetterDataInterface, DataNormalizerInterface
{
public function __construct(
private DataAdapterServiceInterface $dataAdapterService,
private DataServiceInterface $dataService,
private DefinitionResolverInterface $fieldCollectionDefinition,
private Factory $modelFactory
) {
}
Expand Down Expand Up @@ -75,6 +80,40 @@ public function getDataForSetter(
return new Fieldcollection($values, $fieldDefinition->getName());
}

public function normalize(
mixed $value,
Data $fieldDefinition
): ?array {
if (!$value instanceof Fieldcollection) {
return null;
}

$resultItems = [];
$items = $value->getItems();

foreach ($items as $item) {
$type = $item->getType();
$fieldCollectionDefinition = $this->fieldCollectionDefinition->getByKey($item->getType());
if (!$fieldCollectionDefinition) {
continue;
}
$resultItem = ['type' => $type];

foreach ($fieldCollectionDefinition->getFieldDefinitions() as $collectionFieldDefinition) {
$getter = 'get' . ucfirst($collectionFieldDefinition->getName());
$value = $item->$getter();
$resultItem[$collectionFieldDefinition->getName()] = $this->dataService->getNormalizedValue(
$value,
$collectionFieldDefinition,
);
}

$resultItems[] = $resultItem;
}

return $resultItems;
}

/**
* @throws Exception
*/
Expand Down
Loading

0 comments on commit 5680cea

Please sign in to comment.