From c43691a8ae75b0663847a27103409b79bd69ebec Mon Sep 17 00:00:00 2001 From: Elias Date: Tue, 16 Apr 2024 11:00:05 +0200 Subject: [PATCH] Added support to create variants on object creation (#375) * Added support to create variants on object creation * Apply suggestions from code review Co-authored-by: Sebastian Blank * Added error handling when changing existing variants As the parent can be changed for variants this is now catched, as a variant cannot change its parent anymore. * Added support to create variants on object creation * Added error handling when changing existing variants As the parent can be changed for variants this is now catched, as a variant cannot change its parent anymore. * Removed unecessary translations * Addressed some qodana warnings * Allowing variants to be parents of variants of the same class * Apply php-cs-fixer changes * fixup! Allowing variants to be parents of variants of the same class * Added check if parent has changed * Added individual exceptions when variant type cannot be set * Apply php-cs-fixer changes * fixup! Added individual exceptions when variant type cannot be set * Allowing variants to be changed to objects * fixup! Allowing variants to be changed to objects --------- Co-authored-by: Sebastian Blank Co-authored-by: turbo-ele --- src/Resolver/Location/FindParentStrategy.php | 79 +++++++++++++++++++ src/Resolver/Resolver.php | 16 ++++ .../resolver/location/findParent.js | 6 ++ src/Resources/translations/admin.en.yml | 1 + 4 files changed, 102 insertions(+) diff --git a/src/Resolver/Location/FindParentStrategy.php b/src/Resolver/Location/FindParentStrategy.php index 4f653b61..4b794ebf 100644 --- a/src/Resolver/Location/FindParentStrategy.php +++ b/src/Resolver/Location/FindParentStrategy.php @@ -15,9 +15,12 @@ namespace Pimcore\Bundle\DataImporterBundle\Resolver\Location; +use Exception; use Pimcore\Bundle\DataImporterBundle\Exception\InvalidConfigurationException; +use Pimcore\Bundle\DataImporterBundle\Exception\InvalidInputException; use Pimcore\Bundle\DataImporterBundle\Tool\DataObjectLoader; use Pimcore\Model\DataObject; +use Pimcore\Model\DataObject\AbstractObject; use Pimcore\Model\DataObject\ClassDefinition; use Pimcore\Model\Element\ElementInterface; @@ -57,6 +60,8 @@ class FindParentStrategy implements LocationStrategyInterface */ protected $attributeLanguage; + protected bool $saveAsVariant = false; + public function __construct(protected DataObjectLoader $dataObjectLoader) { } @@ -75,6 +80,8 @@ public function setSettings(array $settings): void throw new InvalidConfigurationException('Empty find strategy.'); } + $this->saveAsVariant = isset($settings['asVariant']) && $settings['asVariant'] === 'on'; + $this->findStrategy = $settings['findStrategy']; if ($this->findStrategy == self::FIND_BY_ATTRIBUTE) { @@ -129,6 +136,20 @@ public function updateParent(ElementInterface $element, array $inputData): Eleme } if ($newParent) { + if ( + $newParent->getType() === AbstractObject::OBJECT_TYPE_VARIANT && + ( + $element->getType() !== AbstractObject::OBJECT_TYPE_VARIANT || + $element::class !== $newParent::class + ) + ) { + throw new InvalidInputException( + "An element can only have a variant as a parent if it's a variant itself and of the same class." + ); + } + + $this->setElementType($element, $newParent); + return $element->setParent($newParent); } @@ -138,4 +159,62 @@ public function updateParent(ElementInterface $element, array $inputData): Eleme protected function loadById() { } + + /** + * @throws InvalidInputException + */ + private function setElementType(ElementInterface $element, DataObject | ElementInterface $newParent): void + { + // Check if element should be saved as a variant if not already. + if ( + $this->saveAsVariant && $element->getType() !== AbstractObject::OBJECT_TYPE_VARIANT + ) { + if ( + !$element instanceof DataObject\Concrete + || $element::class !== $newParent::class + ) { + throw new InvalidInputException( + 'Changing type not possible: Only concrete objects of the same class can be saved as a variant.' + ); + } + + if ($element->hasChildren()) { + throw new InvalidInputException( + 'Changing type not possible: Only objects without any children can be saved as a variant.' + ); + } + + if (!$this->getElementClassDefinition($element)?->getAllowVariants()) { + throw new InvalidInputException( + sprintf( + 'Changing type not possible: Class `%s` is not configured to allow the creation of variants.', + $this->getElementClassDefinition($element)?->getName(), + ) + ); + } + + $element->setType(AbstractObject::OBJECT_TYPE_VARIANT); + } elseif ( + !$this->saveAsVariant + && $element instanceof AbstractObject + && $element->getType() === AbstractObject::OBJECT_TYPE_VARIANT + ) { + if ($newParent->getType() === AbstractObject::OBJECT_TYPE_VARIANT) { + throw new InvalidInputException( + 'Changing type not possible: An object cannot be a child of a variant.' + ); + } + + $element->setType(AbstractObject::OBJECT_TYPE_OBJECT); + } + } + + private function getElementClassDefinition(DataObject\Concrete $element): ?ClassDefinition + { + try { + return $element->getClass(); + } catch (Exception) { + return null; + } + } } diff --git a/src/Resolver/Resolver.php b/src/Resolver/Resolver.php index 97af4256..c4da3707 100644 --- a/src/Resolver/Resolver.php +++ b/src/Resolver/Resolver.php @@ -15,10 +15,12 @@ namespace Pimcore\Bundle\DataImporterBundle\Resolver; +use Pimcore\Bundle\DataImporterBundle\Exception\InvalidInputException; use Pimcore\Bundle\DataImporterBundle\Resolver\Factory\FactoryInterface; use Pimcore\Bundle\DataImporterBundle\Resolver\Load\LoadStrategyInterface; use Pimcore\Bundle\DataImporterBundle\Resolver\Location\LocationStrategyInterface; use Pimcore\Bundle\DataImporterBundle\Resolver\Publish\PublishStrategyInterface; +use Pimcore\Model\DataObject\AbstractObject; use Pimcore\Model\Element\ElementInterface; class Resolver @@ -134,6 +136,9 @@ public function loadElementByIdentifier($identifier): ?ElementInterface return $this->getLoadingStrategy()->loadElementByIdentifier($identifier); } + /** + * @throws InvalidInputException + */ public function loadOrCreateAndPrepareElement(array $inputData, bool $createNew = true): ?ElementInterface { $element = $this->loadElement($inputData); @@ -149,7 +154,18 @@ public function loadOrCreateAndPrepareElement(array $inputData, bool $createNew $this->getCreateLocationStrategy()->updateParent($element, $inputData); $justCreated = true; } else { + $oldParentId = $element->getParentId(); $this->getLocationUpdateStrategy()->updateParent($element, $inputData); + + // The parent of a variant cannot be changed anymore. + if ( + $oldParentId !== $element->getParentId() + && $element->getType() === AbstractObject::OBJECT_TYPE_VARIANT + ) { + throw new InvalidInputException( + "Element with id `{$element->getId()}` is a variant and cannot change its parent anymore" + ); + } } $this->getPublishingStrategy()->updatePublishState($element, $justCreated, $inputData); diff --git a/src/Resources/public/js/pimcore/configuration/components/resolver/location/findParent.js b/src/Resources/public/js/pimcore/configuration/components/resolver/location/findParent.js index 17dc33b1..36542c6f 100644 --- a/src/Resources/public/js/pimcore/configuration/components/resolver/location/findParent.js +++ b/src/Resources/public/js/pimcore/configuration/components/resolver/location/findParent.js @@ -154,6 +154,12 @@ pimcore.plugin.pimcoreDataImporterBundle.configuration.components.resolver.locat fieldLabel: t('plugin_pimcore_datahub_data_importer_configpanel_fallback_path'), name: this.dataNamePrefix + 'fallbackPath', value: this.data.fallbackPath + }, + { + xtype: 'checkbox', + fieldLabel: t('plugin_pimcore_datahub_data_importer_configpanel_as_variant'), + name: this.dataNamePrefix + 'asVariant', + value: this.data.asVariant } ] }); diff --git a/src/Resources/translations/admin.en.yml b/src/Resources/translations/admin.en.yml index 84a5161e..700bf11f 100644 --- a/src/Resources/translations/admin.en.yml +++ b/src/Resources/translations/admin.en.yml @@ -64,6 +64,7 @@ plugin_pimcore_datahub_data_importer_configpanel_element_creation: Element Creat plugin_pimcore_datahub_data_importer_configpanel_create_location_strategy: Location Strategy plugin_pimcore_datahub_data_importer_configpanel_fallback_path: Fallback Path plugin_pimcore_datahub_data_importer_configpanel_find_strategy: Find Strategy +plugin_pimcore_datahub_data_importer_configpanel_as_variant: As Variant plugin_pimcore_datahub_data_importer_configpanel_createLocationStrategy.type_findParent: Find Parent plugin_pimcore_datahub_data_importer_configpanel_createLocationStrategy.type_findOrCreateFolder: Find or Create Folder plugin_pimcore_datahub_data_importer_configpanel_createLocationStrategy.type_noChange: No Change