diff --git a/composer.json b/composer.json index c6855529a..d25f64682 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "php": "~8.3.0 || ~8.4.0", "league/csv": "^9.27", "nesbot/carbon": "^3.8.4", - "pimcore/static-resolver-bundle": "^3.5.0 ", + "pimcore/static-resolver-bundle": "^3.6.1 ", "pimcore/generic-data-index-bundle": "^2.5.0", "pimcore/pimcore": "^12.3", "zircote/swagger-php": "^4.8 || ^5.0", diff --git a/doc/03_Extending/06_Data_Objects/01_Field_Definition_Adapters.md b/doc/03_Extending/06_Data_Objects/01_Field_Definition_Adapters.md index e9d6dbe45..c441f7b48 100644 --- a/doc/03_Extending/06_Data_Objects/01_Field_Definition_Adapters.md +++ b/doc/03_Extending/06_Data_Objects/01_Field_Definition_Adapters.md @@ -11,6 +11,8 @@ types. Each field type (input, date, relation, etc.) has an adapter that control - **Saved** — transforming incoming API request data into the format the field definition expects before it is stored. - **Read** — normalizing stored data into an API-friendly response format. +- **Detail page** — providing a detail-page-specific representation that may differ from the + normalized value (falls back to normalization when not implemented). - **Exported** — converting stored data into a string for grid/CSV export. - **Inherited** — resolving inherited values in the data object hierarchy. - **Previewed** — providing preview data for search results. @@ -43,6 +45,7 @@ and can be added when your field type needs the corresponding capability. |---|---|---| | `SetterDataInterface` | **Yes** | Transform API request data for saving | | `DataNormalizerInterface` | No | Customize the API response format | +| `DetailDataInterface` | No | Provide a detail-page-specific value (falls back to `DataNormalizerInterface`) | | `DataExportInterface` | No | Provide a string representation for grid/CSV export | | `DataInheritanceInterface` | No | Resolve inherited values in the object hierarchy | | `SearchPreviewDataInterface` | No | Contribute preview data to search results | @@ -128,6 +131,47 @@ Return the API-friendly representation of the value. --- +### DetailDataInterface + +Implement this interface when the detail page of a data object should display a different +representation than the normalized value. When this interface +is **not** implemented, the system automatically falls back to +`DataNormalizerInterface::normalize()`. + +**When to use:** + +- The detail page requires a richer or more descriptive value than from `normalize()`. + + +```php +namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data; + +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; +use Pimcore\Model\DataObject\ClassDefinition\Data; +use Pimcore\Model\DataObject\Concrete; + +interface DetailDataInterface +{ + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): mixed; +} +``` + +| Parameter | Description | +|---|---| +| `$object` | The data object being loaded for the detail page. | +| `$value` | The raw stored value from the data object getter. | +| `$fieldDefinition` | The Pimcore field definition for this field. | +| `$contextData` | Container context (object brick, localized field, etc.), or `null` for top-level fields. | + +Return the detail-page-specific representation of the value. + +--- + ### DataExportInterface Implement this interface when the field type needs custom handling for grid column display @@ -314,6 +358,7 @@ namespace App\DataObject\Data\Adapter; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataExportInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface; +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DetailDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SearchPreviewDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface; @@ -326,6 +371,7 @@ use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; final readonly class CustomFieldAdapter implements SetterDataInterface, DataNormalizerInterface, + DetailDataInterface, DataExportInterface, DataInheritanceInterface, SearchPreviewDataInterface @@ -374,6 +420,19 @@ final readonly class CustomFieldAdapter implements return $value; } + // --- DetailDataInterface (optional) --- + + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): mixed { + // Return a detail-page-specific representation of the value. + // When this interface is not implemented, normalize() is called instead. + return $value; + } + // --- DataExportInterface (optional) --- public function getExportData( @@ -466,19 +525,19 @@ class required. | `InputQuantityValueAdapter` | `inputQuantityValue` | Setter | | `QuantityValueRangeAdapter` | `quantityValueRange` | Setter | | `UrlSlugAdapter` | `urlSlug` | Setter | -| `CalculatedValueAdapter` | `calculatedValue` | Setter | +| `CalculatedValueAdapter` | `calculatedValue` | Setter, Detail | | `EncryptedFieldAdapter` | `encryptedField` | Setter, Normalizer, Export | | `TableAdapter` | `table` | Setter, SearchPreview | | `StructuredTableAdapter` | `structuredTable` | Setter, Normalizer, SearchPreview | | `BlockAdapter` | `block` | Setter, Normalizer, SearchPreview | | `FieldCollectionsAdapter` | `fieldcollections` | Setter, Normalizer, SearchPreview | -| `ObjectBricksAdapter` | `objectbricks` | Setter, Normalizer, Inheritance, SearchPreview | -| `LocalizedFieldsAdapter` | `localizedfields` | Setter, Normalizer, Inheritance, SearchPreview | -| `ClassificationStoreAdapter` | `classificationstore` | Setter, Normalizer, Inheritance, SearchPreview | +| `ObjectBricksAdapter` | `objectbricks` | Setter, Normalizer, Detail, Inheritance, SearchPreview | +| `LocalizedFieldsAdapter` | `localizedfields` | Setter, Normalizer, Detail, Inheritance, SearchPreview | +| `ClassificationStoreAdapter` | `classificationstore` | Setter, Normalizer, Detail, Inheritance, SearchPreview | **Legend:** Setter = `SetterDataInterface`, Normalizer = `DataNormalizerInterface`, -Export = `DataExportInterface`, Inheritance = `DataInheritanceInterface`, -SearchPreview = `SearchPreviewDataInterface` +Detail = `DetailDataInterface`, Export = `DataExportInterface`, +Inheritance = `DataInheritanceInterface`, SearchPreview = `SearchPreviewDataInterface` :::info diff --git a/src/DataObject/Data/Adapter/CalculatedValueAdapter.php b/src/DataObject/Data/Adapter/CalculatedValueAdapter.php index 62afa0b85..734ec2b23 100644 --- a/src/DataObject/Data/Adapter/CalculatedValueAdapter.php +++ b/src/DataObject/Data/Adapter/CalculatedValueAdapter.php @@ -13,11 +13,16 @@ namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter; +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DetailDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface; +use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\DataObjectServiceResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface; use Pimcore\Model\DataObject\ClassDefinition\Data; +use Pimcore\Model\DataObject\ClassDefinition\Data\CalculatedValue as CalculatedValueDefinition; use Pimcore\Model\DataObject\Concrete; +use Pimcore\Model\DataObject\Data\CalculatedValue; +use Pimcore\Model\DataObject\Objectbrick\Data\AbstractData; use Pimcore\Model\UserInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; @@ -25,8 +30,21 @@ * @internal */ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] -final readonly class CalculatedValueAdapter implements SetterDataInterface +final readonly class CalculatedValueAdapter implements SetterDataInterface, DetailDataInterface { + private const string OWNER_TYPE_OBJECT = 'object'; + + private const string OWNER_TYPE_LOCALIZED_FIELD = 'localizedfield'; + + private const string OWNER_TYPE_OBJECT_BRICK = 'objectbrick'; + + private const string LOCALIZED_FIELDS_NAME = 'localizedfields'; + + public function __construct( + private DataObjectServiceResolverInterface $dataObjectServiceResolver, + ) { + } + public function getDataForSetter( Concrete $element, Data $fieldDefinition, @@ -38,4 +56,72 @@ public function getDataForSetter( ): null { return null; } + + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): ?string { + if (!$fieldDefinition instanceof CalculatedValueDefinition) { + return null; + } + + $calculatedValue = new CalculatedValue($fieldDefinition->getName()); + $this->applyContextualData($calculatedValue, $fieldDefinition, $contextData); + + return $this->dataObjectServiceResolver->getCalculatedFieldValueForEditMode( + $object, + [], + $calculatedValue, + ); + } + + private function applyContextualData( + CalculatedValue $calculatedValue, + CalculatedValueDefinition $fieldDefinition, + ?FieldContextData $contextData, + ): void { + $contextObject = $contextData?->getContextObject(); + + if ($contextObject instanceof AbstractData) { + $calculatedValue->setContextualData( + self::OWNER_TYPE_OBJECT_BRICK, + $contextObject->getFieldname(), + $contextObject->getType(), + $fieldDefinition->getName(), + null, + null, + $fieldDefinition, + ); + + return; + } + + $language = $contextData?->getLanguage(); + + if ($language !== null) { + $calculatedValue->setContextualData( + self::OWNER_TYPE_LOCALIZED_FIELD, + self::LOCALIZED_FIELDS_NAME, + null, + $language, + null, + null, + $fieldDefinition, + ); + + return; + } + + $calculatedValue->setContextualData( + self::OWNER_TYPE_OBJECT, + $fieldDefinition->getName(), + null, + null, + null, + null, + $fieldDefinition, + ); + } } diff --git a/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php b/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php index 777d2d36f..6e7074ac1 100644 --- a/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php +++ b/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php @@ -21,10 +21,12 @@ use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\ServiceResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface; +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DetailDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\InheritanceData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SearchPreviewDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface; +use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\DataObjectServiceResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface; @@ -40,9 +42,11 @@ use Pimcore\Model\DataObject\Classificationstore; use Pimcore\Model\DataObject\Classificationstore as ClassificationstoreModel; use Pimcore\Model\DataObject\Classificationstore\GroupConfig; +use Pimcore\Model\DataObject\Classificationstore\KeyConfig; use Pimcore\Model\DataObject\Classificationstore\KeyGroupRelation; use Pimcore\Model\DataObject\Classificationstore\KeyGroupRelation\Listing as KeyGroupRelationListing; use Pimcore\Model\DataObject\Concrete; +use Pimcore\Model\DataObject\Data\CalculatedValue; use Pimcore\Model\UserInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use function in_array; @@ -54,12 +58,14 @@ final readonly class ClassificationStoreAdapter implements SetterDataInterface, DataNormalizerInterface, + DetailDataInterface, DataInheritanceInterface, SearchPreviewDataInterface { use ValidateObjectDataTrait; public function __construct( + private DataObjectServiceResolverInterface $dataObjectServiceResolver, private DefinitionCacheResolverInterface $definitionCacheResolver, private DataAdapterServiceInterface $dataAdapterService, private DataServiceInterface $dataService, @@ -68,7 +74,7 @@ public function __construct( private LanguageServiceInterface $languageService, private ServiceResolverInterface $serviceResolver, private SecurityServiceInterface $securityService, - private ToolResolverInterface $toolResolver + private ToolResolverInterface $toolResolver, ) { } @@ -116,6 +122,15 @@ public function getDataForSetter( public function normalize( mixed $value, Data $fieldDefinition + ): ?array { + return $this->handleNormalize($value, $fieldDefinition, useComputedValues: true); + } + + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, ): ?array { return $this->handleNormalize($value, $fieldDefinition); } @@ -192,7 +207,8 @@ public function getPreviewFieldData( private function handleNormalize( mixed $value, Data $fieldDefinition, - ?UserInterface $user = null + ?UserInterface $user = null, + bool $useComputedValues = false, ): ?array { if (!$value instanceof ClassificationstoreModel || !$fieldDefinition instanceof ClassificationstoreDefinition @@ -216,7 +232,13 @@ private function handleNormalize( $keys = $this->getClassificationStoreKeysFromGroup($groupId); foreach ($validLanguages as $validLanguage) { foreach ($keys as $key) { - $normalizedValue = $this->getNormalizedValue($value, $groupId, $key, $validLanguage); + $normalizedValue = $this->getResolvedValue( + $value, + $groupId, + $key, + $validLanguage, + $useComputedValues, + ); if ($normalizedValue !== null) { $resultItems[$groupId][$validLanguage][$key->getKeyId()] = $normalizedValue; @@ -440,15 +462,57 @@ private function getClassificationStoreKeysFromGroup(int $groupId): array /** * @throws DatabaseException */ - private function getNormalizedValue( + private function getResolvedValue( ClassificationstoreModel $classificationstore, int $groupId, KeyGroupRelation $key, - string $language + string $language, + bool $useComputedValue = false, ): mixed { + $keyConfig = $this->definitionCacheResolver->get($key->getKeyId()); + if ($keyConfig === null) { + return null; + } + + if ($useComputedValue && $keyConfig->getType() === 'calculatedValue') { + return $this->resolveComputedValue($classificationstore, $groupId, $key, $language, $keyConfig); + } + return $this->getValue($classificationstore, $groupId, $key, $language); } + /** + * @throws DatabaseException + */ + private function resolveComputedValue( + ClassificationstoreModel $classificationstore, + int $groupId, + KeyGroupRelation $key, + string $language, + KeyConfig $keyConfig, + ): mixed { + $childDef = $this->serviceResolver->getFieldDefinitionFromKeyConfig($keyConfig); + if ($childDef === null) { + return null; + } + + $calculatedValue = new CalculatedValue($classificationstore->getFieldname()); + $calculatedValue->setContextualData( + 'classificationstore', + $classificationstore->getFieldname(), + null, + $language, + $groupId, + $key->getKeyId(), + $childDef, + ); + + return $this->dataObjectServiceResolver->getCalculatedFieldValue( + $classificationstore->getObject(), + $calculatedValue, + ); + } + /** * @throws DatabaseException */ diff --git a/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php b/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php index 2e5935a49..e2548d5b2 100644 --- a/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php +++ b/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php @@ -18,6 +18,7 @@ use Pimcore\Bundle\StaticResolverBundle\Lib\ToolResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface; +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DetailDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\BlockData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SearchPreviewDataInterface; @@ -49,6 +50,7 @@ final readonly class LocalizedFieldsAdapter implements SetterDataInterface, DataNormalizerInterface, + DetailDataInterface, DataInheritanceInterface, SearchPreviewDataInterface { @@ -125,42 +127,33 @@ public function normalize( return null; } - $value->loadLazyData(); - $originalValue = $fieldDefinition->normalize($value); - if (empty($originalValue)) { - return null; - } - $languages = $this->languageService->getUserAllowedLanguages( - $value->getObject(), - $this->securityService->getCurrentUser(), - ElementPermissions::LANGUAGE_VIEW_PERMISSIONS + return $this->resolveLocalizedData( + $value, + $fieldDefinition, + fn (mixed $val, Data $fd, string $lang) => $this->dataService->getNormalizedValue($val, $fd), ); + } - $attributes = array_keys(array_merge(...array_values($originalValue))); - $result = []; - foreach ($attributes as $attribute) { - foreach ($languages as $language) { - try { - $localizedValue = $value->getLocalizedValue($attribute, $language); - } catch (Exception $exception) { - throw new DatabaseException( - sprintf( - 'Error while normalizing localized field: %s', - $exception->getMessage() - ) - ); - } - $fieldDefinition = $value->getFieldDefinition($attribute, $value->getContext()); - if ($fieldDefinition === null) { - throw new NotFoundException(type: 'Field Definition', id: $attribute); - } - - $localizedValue = $this->dataService->getNormalizedValue($localizedValue, $fieldDefinition); - $result[$attribute][$language] = $localizedValue; - } + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): ?array { + if (!$value instanceof Localizedfield || !$fieldDefinition instanceof Localizedfields) { + return null; } - return $result; + return $this->resolveLocalizedData( + $value, + $fieldDefinition, + fn (mixed $val, Data $fd, string $lang) => $this->dataService->getDetailValue( + $value->getObject(), + $val, + $fd, + new FieldContextData(language: $lang), + ), + ); } public function getFieldInheritance( @@ -233,6 +226,53 @@ public function getPreviewFieldData( return $data; } + /** + * @throws DatabaseException|NotFoundException + */ + private function resolveLocalizedData( + Localizedfield $value, + Localizedfields $fieldDefinition, + callable $valueResolver, + ): ?array { + $value->loadLazyData(); + $originalValue = $fieldDefinition->normalize($value); + if (empty($originalValue)) { + return null; + } + + $languages = $this->languageService->getUserAllowedLanguages( + $value->getObject(), + $this->securityService->getCurrentUser(), + ElementPermissions::LANGUAGE_VIEW_PERMISSIONS + ); + + $attributes = array_keys(array_merge(...array_values($originalValue))); + $result = []; + foreach ($attributes as $attribute) { + foreach ($languages as $language) { + try { + $localizedValue = $value->getLocalizedValue($attribute, $language); + } catch (Exception $exception) { + throw new DatabaseException( + sprintf( + 'Error while normalizing localized field: %s', + $exception->getMessage() + ) + ); + } + + $childFieldDefinition = $value->getFieldDefinition($attribute, $value->getContext()); + if ($childFieldDefinition === null) { + throw new NotFoundException(type: 'Field Definition', id: $attribute); + } + + $result[$attribute][$language] = $valueResolver($localizedValue, $childFieldDefinition, $language); + } + } + + return $result; + } + private function getAllowedLanguages( Concrete $element, UserInterface $user, diff --git a/src/DataObject/Data/Adapter/ObjectBricksAdapter.php b/src/DataObject/Data/Adapter/ObjectBricksAdapter.php index ca7e02216..506b7e4da 100644 --- a/src/DataObject/Data/Adapter/ObjectBricksAdapter.php +++ b/src/DataObject/Data/Adapter/ObjectBricksAdapter.php @@ -17,6 +17,7 @@ use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\Objectbrick\DefinitionResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface; +use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DetailDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SearchPreviewDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface; @@ -42,6 +43,7 @@ final readonly class ObjectBricksAdapter implements SetterDataInterface, DataNormalizerInterface, + DetailDataInterface, DataInheritanceInterface, SearchPreviewDataInterface { @@ -97,31 +99,31 @@ public function normalize( return null; } - $resultItems = []; - $allowedTypes = $value->getAllowedBrickTypes(); - foreach ($allowedTypes as $type) { - $resultItems[$type] = null; - $item = $value->get($type); - - if (!$item instanceof AbstractData) { - continue; - } - - $definition = $this->definitionResolver->getByKey($type); - if ($definition === null) { - continue; - } + return $this->resolveBrickData( + $value, + fn (mixed $val, Data $fd, AbstractData $brick) => $this->dataService->getNormalizedValue($val, $fd), + ); + } - foreach ($definition->getFieldDefinitions() as $brickFieldDefinition) { - $fieldName = $brickFieldDefinition->getName(); - $resultItems[$type][$fieldName] = $this->dataService->getNormalizedValue( - $item->get($fieldName), - $brickFieldDefinition, - ); - } + public function getDetailData( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): ?array { + if (!$value instanceof Objectbrick) { + return null; } - return $resultItems; + return $this->resolveBrickData( + $value, + fn (mixed $val, Data $fd, AbstractData $brick) => $this->dataService->getDetailValue( + $object, + $val, + $fd, + new FieldContextData($brick), + ), + ); } public function getFieldInheritance( @@ -204,6 +206,39 @@ public function getPreviewFieldData( return $data; } + private function resolveBrickData( + Objectbrick $value, + callable $valueResolver, + ): array { + $resultItems = []; + $allowedTypes = $value->getAllowedBrickTypes(); + + foreach ($allowedTypes as $type) { + $resultItems[$type] = null; + $item = $value->get($type); + + if (!$item instanceof AbstractData) { + continue; + } + + $definition = $this->definitionResolver->getByKey($type); + if ($definition === null) { + continue; + } + + foreach ($definition->getFieldDefinitions() as $brickFieldDefinition) { + $fieldName = $brickFieldDefinition->getName(); + $resultItems[$type][$fieldName] = $valueResolver( + $item->get($fieldName), + $brickFieldDefinition, + $item, + ); + } + } + + return $resultItems; + } + /** * @throws Exception */ diff --git a/src/DataObject/Data/DetailDataInterface.php b/src/DataObject/Data/DetailDataInterface.php new file mode 100644 index 000000000..0ef7bc73e --- /dev/null +++ b/src/DataObject/Data/DetailDataInterface.php @@ -0,0 +1,28 @@ +setAllowVariants($classData->getAllowVariants()); $dataObject->setShowVariants($classData->getShowVariants()); $dataObject->setHasPreview($classData->getHasPreview()); - $dataObject->setObjectData($this->getNormalizedObjectData($element, $fieldDefinitions)); + $dataObject->setObjectData($this->getDetailObjectData($element, $fieldDefinitions)); if ($dataObject instanceof DataObjectDetail) { $dataObject->setDraftData($this->getDraftData($element, $version)); @@ -106,6 +107,20 @@ public function getNormalizedValue( return $fieldDefinition->normalize($value); } + public function getDetailValue( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): mixed { + $adapter = $this->dataAdapterService->tryDataAdapter($fieldDefinition->getFieldType()); + if ($adapter instanceof DetailDataInterface) { + return $adapter->getDetailData($object, $value, $fieldDefinition, $contextData); + } + + return $this->getNormalizedValue($value, $fieldDefinition); + } + /** * {@inheritdoc} */ @@ -268,11 +283,12 @@ private function removeEmptyValues(array $previewFields): array /** * @throws NotFoundException */ - private function getNormalizedObjectData(Concrete $dataObject, array $fieldDefinitions): array + private function getDetailObjectData(Concrete $dataObject, array $fieldDefinitions): array { $data = []; foreach ($fieldDefinitions as $key => $fieldDefinition) { - $data[$key] = $this->getNormalizedValue( + $data[$key] = $this->getDetailValue( + $dataObject, $this->getValidFieldValue($dataObject, $key), $fieldDefinition ); diff --git a/src/DataObject/Service/DataServiceInterface.php b/src/DataObject/Service/DataServiceInterface.php index 0e5fc1015..f4322896c 100644 --- a/src/DataObject/Service/DataServiceInterface.php +++ b/src/DataObject/Service/DataServiceInterface.php @@ -46,6 +46,13 @@ public function getNormalizedValue( Data $fieldDefinition ): mixed; + public function getDetailValue( + Concrete $object, + mixed $value, + Data $fieldDefinition, + ?FieldContextData $contextData = null, + ): mixed; + /** * @throws DatabaseException|NotFoundException */