diff --git a/src/DataObject/Data/Adapter/BlockAdapter.php b/src/DataObject/Data/Adapter/BlockAdapter.php index f77e2982..0b3599ba 100644 --- a/src/DataObject/Data/Adapter/BlockAdapter.php +++ b/src/DataObject/Data/Adapter/BlockAdapter.php @@ -23,6 +23,7 @@ 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\DataObject\Util\Trait\ValidateFieldTypeTrait; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidDataTypeException; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\DataObject\ClassDefinition\Data\Block; @@ -38,6 +39,8 @@ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] final readonly class BlockAdapter implements SetterDataInterface, DataNormalizerInterface { + use ValidateFieldTypeTrait; + public function __construct( private DataAdapterServiceInterface $dataAdapterService, private DataServiceInterface $dataService @@ -148,13 +151,24 @@ private function processBlockElement( $fieldContextData = $this->createFieldContextData($element, $fieldDefinition, $contextData); foreach ($fieldDefinitions as $elementName => $fd) { - $resultElement[$elementName] = $this->createBlockElement( + $adapter = $this->dataAdapterService->tryDataAdapter($fd->getFieldType()); + if (!$adapter) { + continue; + } + + $value = $this->createBlockElement( + $adapter, $element, $fd, $elementName, $blockElement, $fieldContextData ); + if (!$value) { + continue; + } + + $resultElement[$elementName] = $value; } return $resultElement; @@ -164,24 +178,37 @@ private function processBlockElement( * @throws Exception */ private function createBlockElement( + SetterDataInterface $adapter, Concrete $element, Data $fieldDefinition, string $elementName, ?array $blockElement, ?FieldContextData $fieldContextData = null - ): BlockElement { + ): ?BlockElement { $elementType = $fieldDefinition->getFieldtype(); $elementData = $blockElement[$elementName] ?? null; - $adapter = $this->dataAdapterService->getDataAdapter($elementType); - $blockData = $adapter->getDataForSetter( + $data = $adapter->getDataForSetter( $element, $fieldDefinition, $elementName, [$elementName => $elementData], $fieldContextData ); + if (!$this->validateEncryptedField($fieldDefinition, $data)) { + return null; + } - return new BlockElement($elementName, $elementType, $blockData); + return new BlockElement( + $elementName, + $elementType, + $adapter->getDataForSetter( + $element, + $fieldDefinition, + $elementName, + [$elementName => $elementData], + $fieldContextData + ) + ); } } diff --git a/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php b/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php index 62628b67..d13ce368 100644 --- a/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php +++ b/src/DataObject/Data/Adapter/ClassificationStoreAdapter.php @@ -28,6 +28,7 @@ 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\DataObject\Util\Trait\ValidateFieldTypeTrait; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\DatabaseException; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\DataObject\ClassDefinition\Data\Classificationstore as ClassificationstoreDefinition; @@ -47,6 +48,8 @@ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] final readonly class ClassificationStoreAdapter implements SetterDataInterface, DataNormalizerInterface { + use ValidateFieldTypeTrait; + public function __construct( private DefinitionCacheResolverInterface $definitionCacheResolver, private DataAdapterServiceInterface $dataAdapterService, @@ -182,13 +185,20 @@ private function processGroupKeys( continue; } - $adapter = $this->dataAdapterService->getDataAdapter($fieldDefinition->getFieldType()); + $adapter = $this->dataAdapterService->tryDataAdapter($fieldDefinition->getFieldType()); + if ($adapter === null) { + continue; + } + $setterData = $adapter->getDataForSetter( $element, $fieldDefinition, $fieldDefinition->getName(), [$fieldDefinition->getName() => $value] ); + if (!$this->validateEncryptedField($fieldDefinition, $setterData)) { + continue; + } $container->setLocalizedKeyValue($groupId, $keyId, $setterData, $language); } diff --git a/src/DataObject/Data/Adapter/EncryptedFieldAdapter.php b/src/DataObject/Data/Adapter/EncryptedFieldAdapter.php index 59504af4..c150709c 100644 --- a/src/DataObject/Data/Adapter/EncryptedFieldAdapter.php +++ b/src/DataObject/Data/Adapter/EncryptedFieldAdapter.php @@ -21,6 +21,7 @@ use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface; use Pimcore\Model\DataObject\ClassDefinition\Data; +use Pimcore\Model\DataObject\ClassDefinition\Data\EncryptedField as EncryptedFieldDefinition; use Pimcore\Model\DataObject\Concrete; use Pimcore\Model\DataObject\Data\EncryptedField; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; @@ -43,7 +44,7 @@ public function getDataForSetter( array $data, ?FieldContextData $contextData = null ): ?EncryptedField { - if (!($fieldDefinition instanceof Data\EncryptedField)) { + if (!$fieldDefinition instanceof EncryptedFieldDefinition) { return null; } @@ -51,9 +52,33 @@ public function getDataForSetter( if (!$delegateFieldDefinition) { return null; } - $adapter = $this->dataAdapterService->getDataAdapter($delegateFieldDefinition->getFieldType()); - $result = $adapter->getDataForSetter($element, $delegateFieldDefinition, $key, $data); - return new EncryptedField($fieldDefinition->getDelegate(), $result); + return $this->handleDelegatedField( + $element, + $delegateFieldDefinition, + $fieldDefinition, + $key, + $data, + $contextData + ); + } + + private function handleDelegatedField( + Concrete $element, + Data $delegateFieldDefinition, + EncryptedFieldDefinition $fieldDefinition, + string $key, + array $data, + ?FieldContextData $contextData = null + ): ?EncryptedField { + $adapter = $this->dataAdapterService->tryDataAdapter($fieldDefinition->getFieldType()); + if ($adapter instanceof SetterDataInterface) { + return new EncryptedField( + $fieldDefinition->getDelegate(), + $adapter->getDataForSetter($element, $delegateFieldDefinition, $key, $data, $contextData) + ); + } + + return null; } } diff --git a/src/DataObject/Data/Adapter/FieldCollectionsAdapter.php b/src/DataObject/Data/Adapter/FieldCollectionsAdapter.php index 969bd06f..7791d172 100644 --- a/src/DataObject/Data/Adapter/FieldCollectionsAdapter.php +++ b/src/DataObject/Data/Adapter/FieldCollectionsAdapter.php @@ -24,6 +24,7 @@ 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\DataObject\Util\Trait\ValidateFieldTypeTrait; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\DataObject\ClassDefinition\Data\Fieldcollections; use Pimcore\Model\DataObject\Concrete; @@ -38,6 +39,8 @@ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] final readonly class FieldCollectionsAdapter implements SetterDataInterface, DataNormalizerInterface { + use ValidateFieldTypeTrait; + public function __construct( private DataAdapterServiceInterface $dataAdapterService, private DataServiceInterface $dataService, @@ -146,18 +149,23 @@ private function processCollectionRaw( foreach ($collectionDef?->getFieldDefinitions() as $elementName => $fd) { $elementValue = $blockElement[$elementName] ?? null; - if (!$elementValue) { + $adapter = $this->dataAdapterService->tryDataAdapter($fd->getFieldType()); + if (!$elementValue || !$adapter) { continue; } - $adapter = $this->dataAdapterService->getDataAdapter($fd->getFieldType()); - $collectionData[$elementName] = $adapter->getDataForSetter( + $value = $adapter->getDataForSetter( $element, $fd, $elementName, [$elementName => $elementValue], $fieldContextData ); + if (!$this->validateEncryptedField($fieldDefinition, $value)) { + continue; + } + + $collectionData[$elementName] = $value; } return $collectionData; diff --git a/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php b/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php index d2c2cca9..fa2ce28c 100644 --- a/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php +++ b/src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php @@ -23,6 +23,7 @@ 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\DataObject\Util\Trait\ValidateFieldTypeTrait; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\DatabaseException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; @@ -43,6 +44,8 @@ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] final readonly class LocalizedFieldsAdapter implements SetterDataInterface, DataNormalizerInterface { + use ValidateFieldTypeTrait; + public function __construct( private DataAdapterServiceInterface $dataAdapterService, private DataServiceInterface $dataService, @@ -75,18 +78,23 @@ public function getDataForSetter( continue; } - $adapter = $this->dataAdapterService->getDataAdapter($childFieldDefinition->getFieldType()); - $localizedField->setLocalizedValue( + $adapter = $this->dataAdapterService->tryDataAdapter($childFieldDefinition->getFieldType()); + if (!$adapter) { + continue; + } + + $value = $adapter->getDataForSetter( + $element, + $childFieldDefinition, $name, - $adapter->getDataForSetter( - $element, - $childFieldDefinition, - $name, - [$name => $fieldData], - new FieldContextData(language: $language) - ), - $language + [$name => $fieldData], + new FieldContextData(language: $language) ); + if (!$this->validateEncryptedField($childFieldDefinition, $value)) { + continue; + } + + $localizedField->setLocalizedValue($name, $value, $language); } } diff --git a/src/DataObject/Data/Adapter/ObjectBricksAdapter.php b/src/DataObject/Data/Adapter/ObjectBricksAdapter.php index e20dba9a..64a04ae7 100644 --- a/src/DataObject/Data/Adapter/ObjectBricksAdapter.php +++ b/src/DataObject/Data/Adapter/ObjectBricksAdapter.php @@ -24,6 +24,7 @@ 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\DataObject\Util\Trait\ValidateFieldTypeTrait; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\DataObject\ClassDefinition\Data\Objectbricks; use Pimcore\Model\DataObject\Concrete; @@ -39,6 +40,8 @@ #[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)] final readonly class ObjectBricksAdapter implements SetterDataInterface, DataNormalizerInterface { + use ValidateFieldTypeTrait; + public function __construct( private DataAdapterServiceInterface $dataAdapterService, private DataServiceInterface $dataService, @@ -177,19 +180,24 @@ private function getCollectionData( ): array { $collectionData = []; foreach ($collectionDef->getFieldDefinitions() as $fd) { + $adapter = $this->dataAdapterService->tryDataAdapter($fd->getFieldType()); $fieldName = $fd->getName(); - if (!array_key_exists($fieldName, $rawData)) { + if (!array_key_exists($fieldName, $rawData) || !$adapter) { continue; } - $adapter = $this->dataAdapterService->getDataAdapter($fd->getFieldType()); - $collectionData[$fd->getName()] = $adapter->getDataForSetter( + $value = $adapter->getDataForSetter( $element, $fd, $fieldName, [$fieldName => $rawData[$fieldName]], new FieldContextData($brick) ); + if (!$this->validateEncryptedField($fd, $value)) { + continue; + } + + $collectionData[$fieldName] = $value; } return $collectionData; diff --git a/src/DataObject/Service/DataAdapterService.php b/src/DataObject/Service/DataAdapterService.php index cac29b63..76813f0a 100644 --- a/src/DataObject/Service/DataAdapterService.php +++ b/src/DataObject/Service/DataAdapterService.php @@ -54,10 +54,23 @@ public function getFieldDefinitionAdapterClass(string $fieldDefinitionType): str ); } + /** + * @throws InvalidArgumentException + */ public function getDataAdapter(string $fieldDefinitionType): SetterDataInterface { return $this->dataAdapterLoader->loadAdapter( $this->getFieldDefinitionAdapterClass($fieldDefinitionType) ); } + + // ToDo: Consider removing this method and using getDataAdapter when field types from bundles are implemented + public function tryDataAdapter(string $fieldDefinitionType): ?SetterDataInterface + { + try { + return $this->getDataAdapter($fieldDefinitionType); + } catch (InvalidArgumentException) { + return null; + } + } } diff --git a/src/DataObject/Service/DataAdapterServiceInterface.php b/src/DataObject/Service/DataAdapterServiceInterface.php index 6fdfffed..c3968ab6 100644 --- a/src/DataObject/Service/DataAdapterServiceInterface.php +++ b/src/DataObject/Service/DataAdapterServiceInterface.php @@ -31,5 +31,10 @@ public function getAdaptersMapping(): array; */ public function getFieldDefinitionAdapterClass(string $fieldDefinitionType): string; + /** + * @throws InvalidArgumentException + */ public function getDataAdapter(string $fieldDefinitionType): SetterDataInterface; + + public function tryDataAdapter(string $fieldDefinitionType): ?SetterDataInterface; } diff --git a/src/DataObject/Service/DataService.php b/src/DataObject/Service/DataService.php index dd28f6f0..fee605de 100644 --- a/src/DataObject/Service/DataService.php +++ b/src/DataObject/Service/DataService.php @@ -49,10 +49,12 @@ public function getObjectData(Concrete $dataObject): array foreach ($fieldDefinitions as $key => $fieldDefinition) { try { - $data[$key] = $this->getNormalizedValue($dataObject->get($key), $fieldDefinition); + $value = $dataObject->get($key); } catch (Exception) { throw new NotFoundException(type: 'field', id: $key); } + + $data[$key] = $this->getNormalizedValue($value, $fieldDefinition); } return $data; @@ -85,7 +87,7 @@ public function getNormalizedValue( return null; } - $adapter = $this->dataAdapterService->getDataAdapter($fieldDefinition->getFieldType()); + $adapter = $this->dataAdapterService->tryDataAdapter($fieldDefinition->getFieldType()); if ($adapter instanceof DataNormalizerInterface) { return $adapter->normalize($value, $fieldDefinition); } diff --git a/src/DataObject/Service/Loader/TaggedIteratorDataAdapter.php b/src/DataObject/Service/Loader/TaggedIteratorDataAdapter.php index ac7e84b2..3de81935 100644 --- a/src/DataObject/Service/Loader/TaggedIteratorDataAdapter.php +++ b/src/DataObject/Service/Loader/TaggedIteratorDataAdapter.php @@ -16,9 +16,9 @@ namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Service\Loader; -use InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface; +use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use function get_class; use function sprintf; diff --git a/src/DataObject/Util/Trait/ValidateFieldTypeTrait.php b/src/DataObject/Util/Trait/ValidateFieldTypeTrait.php new file mode 100644 index 00000000..a7f6b6bd --- /dev/null +++ b/src/DataObject/Util/Trait/ValidateFieldTypeTrait.php @@ -0,0 +1,32 @@ +getClass(); foreach ($editableData as $key => $value) { $fieldDefinition = $class->getFieldDefinition($key); - if ($fieldDefinition === null) { + if ($fieldDefinition === null || !array_key_exists($key, $editableData)) { continue; } - $data = array_key_exists($key, $editableData) - ? $this->dataAdapterService - ->getDataAdapter($fieldDefinition->getFieldType()) - ->getDataForSetter($element, $fieldDefinition, $key, $editableData) - : null; + $adapter = $this->dataAdapterService->tryDataAdapter($fieldDefinition->getFieldtype()); + if ($adapter === null) { + continue; + } + + $value = $adapter->getDataForSetter($element, $fieldDefinition, $key, $editableData); + if (!$this->validateEncryptedField($fieldDefinition, $value)) { + continue; + } - $element->setValue($key, $data); + $element->setValue($key, $value); } } catch (Exception $e) {