From e229c44352bb210d414c1e6868c7274f963c071e Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 6 Oct 2021 18:19:41 +0200 Subject: [PATCH 1/2] Refractor dimension content collection behaviour --- .../ContentDataMapper/ContentDataMapper.php | 11 +-- .../DataMapper/RoutableDataMapper.php | 1 - .../ContentManager/ContentManager.php | 5 +- .../ContentManagerInterface.php | 5 +- .../ContentPersister/ContentPersister.php | 29 +++--- .../ContentPersisterInterface.php | 4 +- .../DimensionContentCollectionFactory.php | 98 ++++--------------- ...nsionContentCollectionFactoryInterface.php | 3 +- .../Model/ContentRichEntityInterface.php | 1 + .../Model/DimensionContentCollection.php | 94 +++++++++++++++--- .../DimensionContentCollectionInterface.php | 7 ++ Resources/config/services.xml | 4 +- UPGRADE.md | 16 +++ 13 files changed, 148 insertions(+), 130 deletions(-) diff --git a/Content/Application/ContentDataMapper/ContentDataMapper.php b/Content/Application/ContentDataMapper/ContentDataMapper.php index d654912b..7ce53d2a 100644 --- a/Content/Application/ContentDataMapper/ContentDataMapper.php +++ b/Content/Application/ContentDataMapper/ContentDataMapper.php @@ -39,13 +39,10 @@ public function map( $localizedDimensionAttributes = $dimensionAttributes; $unlocalizedDimensionAttributes = $dimensionAttributes; $unlocalizedDimensionAttributes['locale'] = null; - $unlocalizedDimensionContent = $dimensionContentCollection->getDimensionContent($unlocalizedDimensionAttributes); - $localizedDimensionContent = $dimensionContentCollection->getDimensionContent($localizedDimensionAttributes); - - if (!$unlocalizedDimensionContent || !$localizedDimensionContent) { - // TODO see https://github.com/sulu/SuluContentBundle/pull/204 - throw new \RuntimeException('Create unlocalized and localized dimension content.'); - } + $unlocalizedDimensionContent = $dimensionContentCollection->getDimensionContent($unlocalizedDimensionAttributes) + ?: $dimensionContentCollection->createDimensionContent($unlocalizedDimensionAttributes); + $localizedDimensionContent = $dimensionContentCollection->getDimensionContent($localizedDimensionAttributes) + ?: $dimensionContentCollection->createDimensionContent($localizedDimensionAttributes); foreach ($this->dataMappers as $mapper) { $mapper->map($unlocalizedDimensionContent, $localizedDimensionContent, $data); diff --git a/Content/Application/ContentDataMapper/DataMapper/RoutableDataMapper.php b/Content/Application/ContentDataMapper/DataMapper/RoutableDataMapper.php index fc656d20..08f43524 100644 --- a/Content/Application/ContentDataMapper/DataMapper/RoutableDataMapper.php +++ b/Content/Application/ContentDataMapper/DataMapper/RoutableDataMapper.php @@ -117,7 +117,6 @@ public function map( $entityClass = null; $routeSchema = null; $resourceKey = $localizedDimensionContent::getResourceKey(); - foreach ($this->routeMappings as $key => $mapping) { if ($resourceKey === $mapping['resource_key']) { $entityClass = $mapping['entityClass'] ?? $key; diff --git a/Content/Application/ContentManager/ContentManager.php b/Content/Application/ContentManager/ContentManager.php index b78c6051..77a400c8 100644 --- a/Content/Application/ContentManager/ContentManager.php +++ b/Content/Application/ContentManager/ContentManager.php @@ -20,6 +20,7 @@ use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\ContentResolverInterface; use Sulu\Bundle\ContentBundle\Content\Application\ContentWorkflow\ContentWorkflowInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; class ContentManager implements ContentManagerInterface @@ -75,9 +76,9 @@ public function resolve(ContentRichEntityInterface $contentRichEntity, array $di return $this->contentResolver->resolve($contentRichEntity, $dimensionAttributes); } - public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface + public function persist(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, array $data): DimensionContentCollectionInterface { - return $this->contentPersister->persist($contentRichEntity, $data, $dimensionAttributes); + return $this->contentPersister->persist($contentRichEntity, $dimensionAttributes, $data); } public function normalize(DimensionContentInterface $dimensionContent): array diff --git a/Content/Application/ContentManager/ContentManagerInterface.php b/Content/Application/ContentManager/ContentManagerInterface.php index f40bb01b..df6e744d 100644 --- a/Content/Application/ContentManager/ContentManagerInterface.php +++ b/Content/Application/ContentManager/ContentManagerInterface.php @@ -14,6 +14,7 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\ContentManager; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; interface ContentManagerInterface @@ -24,10 +25,10 @@ interface ContentManagerInterface public function resolve(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes): DimensionContentInterface; /** - * @param mixed[] $data * @param mixed[] $dimensionAttributes + * @param mixed[] $data */ - public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface; + public function persist(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, array $data): DimensionContentCollectionInterface; /** * @return mixed[] diff --git a/Content/Application/ContentPersister/ContentPersister.php b/Content/Application/ContentPersister/ContentPersister.php index c06317aa..ca98d733 100644 --- a/Content/Application/ContentPersister/ContentPersister.php +++ b/Content/Application/ContentPersister/ContentPersister.php @@ -13,10 +13,10 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\ContentPersister; -use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\ContentMergerInterface; +use Sulu\Bundle\ContentBundle\Content\Application\ContentDataMapper\ContentDataMapperInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Factory\DimensionContentCollectionFactoryInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; -use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface; class ContentPersister implements ContentPersisterInterface { @@ -26,34 +26,27 @@ class ContentPersister implements ContentPersisterInterface private $dimensionContentCollectionFactory; /** - * @var ContentMergerInterface + * @var ContentDataMapperInterface */ - private $contentMerger; + private $contentDataMapper; public function __construct( DimensionContentCollectionFactoryInterface $dimensionContentCollectionFactory, - ContentMergerInterface $contentMerger + ContentDataMapperInterface $contentDataMapper ) { $this->dimensionContentCollectionFactory = $dimensionContentCollectionFactory; - $this->contentMerger = $contentMerger; + $this->contentDataMapper = $contentDataMapper; } - public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface + public function persist(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, array $data): DimensionContentCollectionInterface { - /* - * Data should always be persisted to the STAGE_DRAFT content-dimension of the given $dimensionAttributes. - * Modifying data of other content-dimensions (eg. STAGE_LIVE) should only be possible by applying transitions - * of the ContentWorkflow. - * - * TODO: maybe throw an exception here if the $dimensionAttributes contain another stage than 'STAGE_DRAFT' - */ - $dimensionContentCollection = $this->dimensionContentCollectionFactory->create( $contentRichEntity, - $dimensionAttributes, - $data + $dimensionAttributes ); - return $this->contentMerger->merge($dimensionContentCollection); + $this->contentDataMapper->map($dimensionContentCollection, $dimensionAttributes, $data); + + return $dimensionContentCollection; } } diff --git a/Content/Application/ContentPersister/ContentPersisterInterface.php b/Content/Application/ContentPersister/ContentPersisterInterface.php index 0cfea457..8793b21d 100644 --- a/Content/Application/ContentPersister/ContentPersisterInterface.php +++ b/Content/Application/ContentPersister/ContentPersisterInterface.php @@ -14,7 +14,7 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\ContentPersister; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; -use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface; interface ContentPersisterInterface { @@ -22,5 +22,5 @@ interface ContentPersisterInterface * @param mixed[] $data * @param mixed[] $dimensionAttributes */ - public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface; + public function persist(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, array $data): DimensionContentCollectionInterface; } diff --git a/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php b/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php index 18964c14..980e6ee4 100644 --- a/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php +++ b/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php @@ -13,115 +13,51 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\DimensionContentCollectionFactory; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; -use Sulu\Bundle\ContentBundle\Content\Application\ContentDataMapper\ContentDataMapperInterface; +use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\Merger\MergerInterface; +use Sulu\Bundle\ContentBundle\Content\Application\ContentMetadataInspector\ContentMetadataInspectorInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Factory\DimensionContentCollectionFactoryInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollection; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface; -use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; -use Sulu\Bundle\ContentBundle\Content\Domain\Repository\DimensionContentRepositoryInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; -use Symfony\Component\PropertyAccess\PropertyAccessorInterface; class DimensionContentCollectionFactory implements DimensionContentCollectionFactoryInterface { /** - * @var DimensionContentRepositoryInterface + * @var ContentMetadataInspectorInterface */ - private $dimensionContentRepository; + private $contentMetadataInspector; /** - * @var ContentDataMapperInterface + * @var iterable */ - private $contentDataMapper; + private $mergers; /** - * @var PropertyAccessorInterface + * @var PropertyAccessor */ private $propertyAccessor; public function __construct( - DimensionContentRepositoryInterface $dimensionContentRepository, - ContentDataMapperInterface $contentDataMapper, + iterable $mergers, + ContentMetadataInspectorInterface $contentMetadataInspector, PropertyAccessor $propertyAccessor ) { - $this->dimensionContentRepository = $dimensionContentRepository; - $this->contentDataMapper = $contentDataMapper; + $this->mergers = $mergers; + $this->contentMetadataInspector = $contentMetadataInspector; $this->propertyAccessor = $propertyAccessor; } public function create( ContentRichEntityInterface $contentRichEntity, - array $dimensionAttributes, - array $data + array $dimensionAttributes ): DimensionContentCollectionInterface { - $dimensionContentCollection = $this->dimensionContentRepository->load($contentRichEntity, $dimensionAttributes); - $dimensionAttributes = $dimensionContentCollection->getDimensionAttributes(); - - $orderedContentDimensions = \iterator_to_array($dimensionContentCollection); - $dimensionContents = new ArrayCollection($orderedContentDimensions); - - $unlocalizedAttributes = $dimensionAttributes; - $unlocalizedAttributes['locale'] = null; - - // get or create unlocalized dimension content - $unlocalizedDimensionContent = $dimensionContentCollection->getDimensionContent($unlocalizedAttributes); - - if (!$unlocalizedDimensionContent) { - $unlocalizedDimensionContent = $this->createContentDimension( - $contentRichEntity, - $dimensionContents, - $unlocalizedAttributes - ); - $orderedContentDimensions[] = $unlocalizedDimensionContent; - } - - $localizedDimensionContent = null; - if (isset($dimensionAttributes['locale'])) { - // get or create localized dimension content - $localizedDimensionContent = $dimensionContentCollection->getDimensionContent($dimensionAttributes); - - if (!$localizedDimensionContent) { - $localizedDimensionContent = $this->createContentDimension( - $contentRichEntity, - $dimensionContents, - $dimensionAttributes - ); - $orderedContentDimensions[] = $localizedDimensionContent; - } - } - - $dimensionContentCollection = new DimensionContentCollection( - $orderedContentDimensions, + return new DimensionContentCollection( + $contentRichEntity, $dimensionAttributes, - $dimensionContentCollection->getDimensionContentClass() + $this->contentMetadataInspector->getDimensionContentClass(\get_class($this)), + $this->mergers, + $this->propertyAccessor ); - - $this->contentDataMapper->map($dimensionContentCollection, $dimensionAttributes, $data); - - return $dimensionContentCollection; - } - - /** - * @param Collection $dimensionContents - * @param mixed[] $attributes - */ - private function createContentDimension( - ContentRichEntityInterface $contentRichEntity, - Collection $dimensionContents, - array $attributes - ): DimensionContentInterface { - $dimensionContent = $contentRichEntity->createDimensionContent(); - - foreach ($attributes as $attributeName => $attributeValue) { - $this->propertyAccessor->setValue($dimensionContent, $attributeName, $attributeValue); - } - - $contentRichEntity->addDimensionContent($dimensionContent); - $dimensionContents->add($dimensionContent); - - return $dimensionContent; } } diff --git a/Content/Domain/Factory/DimensionContentCollectionFactoryInterface.php b/Content/Domain/Factory/DimensionContentCollectionFactoryInterface.php index 91b52620..24386fc2 100644 --- a/Content/Domain/Factory/DimensionContentCollectionFactoryInterface.php +++ b/Content/Domain/Factory/DimensionContentCollectionFactoryInterface.php @@ -24,7 +24,6 @@ interface DimensionContentCollectionFactoryInterface */ public function create( ContentRichEntityInterface $contentRichEntity, - array $dimensionAttributes, - array $data + array $dimensionAttributes ): DimensionContentCollectionInterface; } diff --git a/Content/Domain/Model/ContentRichEntityInterface.php b/Content/Domain/Model/ContentRichEntityInterface.php index ba95aabb..1a961fb2 100644 --- a/Content/Domain/Model/ContentRichEntityInterface.php +++ b/Content/Domain/Model/ContentRichEntityInterface.php @@ -14,6 +14,7 @@ namespace Sulu\Bundle\ContentBundle\Content\Domain\Model; use Doctrine\Common\Collections\Collection; +use Sulu\Bundle\ContentBundle\Content\Domain\Factory\DimensionContentCollectionFactoryInterface; interface ContentRichEntityInterface { diff --git a/Content/Domain/Model/DimensionContentCollection.php b/Content/Domain/Model/DimensionContentCollection.php index 5deea355..a48ebc2f 100644 --- a/Content/Domain/Model/DimensionContentCollection.php +++ b/Content/Domain/Model/DimensionContentCollection.php @@ -15,7 +15,9 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; +use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\Merger\MergerInterface; use Sulu\Component\Util\SortUtils; +use Symfony\Component\PropertyAccess\PropertyAccessor; /** * @implements \IteratorAggregate @@ -23,9 +25,9 @@ class DimensionContentCollection implements \IteratorAggregate, DimensionContentCollectionInterface { /** - * @var ArrayCollection + * @var ContentRichEntityInterface */ - private $dimensionContents; + private $contentRichEntity; /** * @var mixed[] @@ -42,26 +44,37 @@ class DimensionContentCollection implements \IteratorAggregate, DimensionContent */ private $defaultDimensionAttributes; + /** + * @var iterable + */ + private $mergers; + + /** + * @var PropertyAccessor + */ + private $propertyAccessor; + /** * DimensionContentCollection constructor. * - * @param DimensionContentInterface[] $dimensionContents + * @param ContentRichEntityInterface $contentRichEntity * @param mixed[] $dimensionAttributes * @param class-string $dimensionContentClass + * @param iterable */ public function __construct( - array $dimensionContents, + ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, - string $dimensionContentClass + string $dimensionContentClass, + iterable $mergers, + PropertyAccessor $propertyAccessor ) { + $this->contentRichEntity = $contentRichEntity; $this->dimensionContentClass = $dimensionContentClass; $this->defaultDimensionAttributes = $dimensionContentClass::getDefaultDimensionAttributes(); $this->dimensionAttributes = $dimensionContentClass::getEffectiveDimensionAttributes($dimensionAttributes); - - $this->dimensionContents = new ArrayCollection( - // dimension contents need to be sorted from most specific to least specific when they are merged - SortUtils::multisort($dimensionContents, \array_keys($this->dimensionAttributes), 'asc') - ); + $this->mergers = $mergers; + $this->propertyAccessor = $propertyAccessor; } public function getDimensionContentClass(): string @@ -84,7 +97,20 @@ public function getDimensionContent(array $dimensionAttributes): ?DimensionConte $criteria->andWhere($expr); } - return $this->dimensionContents->matching($criteria)->first() ?: null; + return $this->contentRichEntity->getDimensionContents()->matching($criteria)->first() ?: null; + } + + public function createDimensionContent(array $dimensionAttributes): DimensionContentInterface + { + $dimensionContent = $this->contentRichEntity->createDimensionContent(); + + foreach ($dimensionAttributes as $attributeName => $attributeValue) { + $this->propertyAccessor->setValue($dimensionContent, $attributeName, $attributeValue); + } + + $this->contentRichEntity->addDimensionContent($dimensionContent); + + return $dimensionContent; } public function getDimensionAttributes(): array @@ -94,11 +120,53 @@ public function getDimensionAttributes(): array public function getIterator() { - return $this->dimensionContents; + return new ArrayCollection(SortUtils::multisort( + $this->contentRichEntity->getDimensionContents()->toArray(), + \array_keys($this->dimensionAttributes), 'asc' + )); } public function count(): int { - return \count($this->dimensionContents); + return $this->contentRichEntity->getDimensionContents()->count(); + } + + public function getMergedDimensionContent(): DimensionContentInterface + { + $unlocalizedDimensionAttributes = $this->dimensionAttributes; + $unlocalizedDimensionAttributes['locale'] = null; + + $dimensionContents = \array_filter([ + $this->getDimensionContent($unlocalizedDimensionAttributes), + $this->getDimensionContent($this->dimensionAttributes), + ]); + + $mergedDimensionContent = null; + + foreach ($dimensionContents as $dimensionContent) { + if (!$mergedDimensionContent) { + $contentRichEntity = $dimensionContent->getResource(); + $mergedDimensionContent = $contentRichEntity->createDimensionContent(); + $mergedDimensionContent->markAsMerged(); + } + + foreach ($this->mergers as $merger) { + $merger->merge($mergedDimensionContent, $dimensionContent); + } + + foreach ($this->dimensionAttributes as $key => $value) { + $this->propertyAccessor->setValue( + $mergedDimensionContent, + $key, + $this->propertyAccessor->getValue($dimensionContent, $key) + ); + } + } + + if (!$mergedDimensionContent) { + throw new \RuntimeException('Expected at least one dimensionContent given.'); + } + + return $mergedDimensionContent; } } diff --git a/Content/Domain/Model/DimensionContentCollectionInterface.php b/Content/Domain/Model/DimensionContentCollectionInterface.php index 7ed53e14..5caef7a7 100644 --- a/Content/Domain/Model/DimensionContentCollectionInterface.php +++ b/Content/Domain/Model/DimensionContentCollectionInterface.php @@ -23,6 +23,11 @@ interface DimensionContentCollectionInterface extends \Traversable, \Countable */ public function getDimensionContent(array $dimensionAttributes): ?DimensionContentInterface; + /** + * @param mixed[] $dimensionAttributes + */ + public function createDimensionContent(array $dimensionAttributes): DimensionContentInterface; + /** * @return class-string */ @@ -32,4 +37,6 @@ public function getDimensionContentClass(): string; * @return mixed[] */ public function getDimensionAttributes(): array; + + public function getMergedDimensionContent(): DimensionContentInterface; } diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 362934e1..b7daefce 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -54,7 +54,7 @@ - + @@ -108,7 +108,7 @@ - + diff --git a/UPGRADE.md b/UPGRADE.md index 62a49bec..52fb4b20 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,22 @@ ## 0.7.0 +### ContentPersisterInterface and ContentManagerInterface changed + +The `ContentPersisterInterface` and `ContentManagerInterface` was changed to be similar to the ContentMapper: + +**Before** + +```php + public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface; +``` + +**After** + +```php + public function persist(ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, array $data): DimensionContentCollectionInterface; +``` + ### ContentMapperInterface changed The `ContentMapperInterface` was changed as a preparation for refactoring the `DimensionContentCollection`: From 6f53ad4a8e7ed08e05c7c0ef2ac6f881c5ce4934 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 13 Oct 2021 11:25:06 +0200 Subject: [PATCH 2/2] Refractor dimension content collection --- .../ContentCopier/ContentCopier.php | 2 +- .../ContentMerger/ContentMerger.php | 28 +------- .../DimensionContentCollectionFactory.php | 12 +--- .../Model/ContentRichEntityInterface.php | 10 +++ .../Domain/Model/ContentRichEntityTrait.php | 19 ++++++ .../Model/DimensionContentCollection.php | 68 ++----------------- .../DimensionContentCollectionInterface.php | 17 +---- .../Domain/Model/DimensionContentTrait.php | 9 +++ Resources/config/services.xml | 1 - .../Controller/ExampleController.php | 4 +- .../ExampleTestBundle/Entity/Example.php | 5 ++ .../ContentCopier/ContentCopierTest.php | 6 +- .../ContentManager/ContentManagerTest.php | 4 +- .../ContentPersister/ContentPersisterTest.php | 2 +- .../Model/DimensionContentCollectionTest.php | 21 ------ 15 files changed, 65 insertions(+), 143 deletions(-) diff --git a/Content/Application/ContentCopier/ContentCopier.php b/Content/Application/ContentCopier/ContentCopier.php index 8056681a..3c6bb169 100644 --- a/Content/Application/ContentCopier/ContentCopier.php +++ b/Content/Application/ContentCopier/ContentCopier.php @@ -83,6 +83,6 @@ public function copyFromDimensionContent( ): DimensionContentInterface { $data = $this->contentNormalizer->normalize($dimensionContent); - return $this->contentPersister->persist($targetContentRichEntity, $data, $targetDimensionAttributes); + return $this->contentPersister->persist($targetContentRichEntity, $targetDimensionAttributes, $data); } } diff --git a/Content/Application/ContentMerger/ContentMerger.php b/Content/Application/ContentMerger/ContentMerger.php index 508088ba..5ec1da67 100644 --- a/Content/Application/ContentMerger/ContentMerger.php +++ b/Content/Application/ContentMerger/ContentMerger.php @@ -43,32 +43,6 @@ public function __construct( public function merge(DimensionContentCollectionInterface $dimensionContentCollection): DimensionContentInterface { - if (!$dimensionContentCollection->count()) { - throw new \RuntimeException('Expected at least one dimensionContent given.'); - } - - $mergedDimensionContent = null; - - foreach ($dimensionContentCollection as $dimensionContent) { - if (!$mergedDimensionContent) { - $contentRichEntity = $dimensionContent->getResource(); - $mergedDimensionContent = $contentRichEntity->createDimensionContent(); - $mergedDimensionContent->markAsMerged(); - } - - foreach ($this->mergers as $merger) { - $merger->merge($mergedDimensionContent, $dimensionContent); - } - - foreach ($dimensionContentCollection->getDimensionAttributes() as $key => $value) { - $this->propertyAccessor->setValue( - $mergedDimensionContent, - $key, - $this->propertyAccessor->getValue($dimensionContent, $key) - ); - } - } - - return $mergedDimensionContent; + return $dimensionContentCollection->getMergedDimensionContent(); } } diff --git a/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php b/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php index 980e6ee4..7dec6124 100644 --- a/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php +++ b/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php @@ -14,7 +14,6 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\DimensionContentCollectionFactory; use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\Merger\MergerInterface; -use Sulu\Bundle\ContentBundle\Content\Application\ContentMetadataInspector\ContentMetadataInspectorInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Factory\DimensionContentCollectionFactoryInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollection; @@ -23,11 +22,6 @@ class DimensionContentCollectionFactory implements DimensionContentCollectionFactoryInterface { - /** - * @var ContentMetadataInspectorInterface - */ - private $contentMetadataInspector; - /** * @var iterable */ @@ -38,13 +32,14 @@ class DimensionContentCollectionFactory implements DimensionContentCollectionFac */ private $propertyAccessor; + /** + * @param iterable $mergers + */ public function __construct( iterable $mergers, - ContentMetadataInspectorInterface $contentMetadataInspector, PropertyAccessor $propertyAccessor ) { $this->mergers = $mergers; - $this->contentMetadataInspector = $contentMetadataInspector; $this->propertyAccessor = $propertyAccessor; } @@ -55,7 +50,6 @@ public function create( return new DimensionContentCollection( $contentRichEntity, $dimensionAttributes, - $this->contentMetadataInspector->getDimensionContentClass(\get_class($this)), $this->mergers, $this->propertyAccessor ); diff --git a/Content/Domain/Model/ContentRichEntityInterface.php b/Content/Domain/Model/ContentRichEntityInterface.php index 1a961fb2..41dea1e3 100644 --- a/Content/Domain/Model/ContentRichEntityInterface.php +++ b/Content/Domain/Model/ContentRichEntityInterface.php @@ -28,6 +28,16 @@ public function getId(); */ public function getDimensionContents(): Collection; + /** + * @param mixed[] $dimensionAttributes + */ + public function findDimensionContent(array $dimensionAttributes): ?DimensionContentInterface; + + /** + * @return class-string + */ + public static function getDimensionContentClass(): string; + public function createDimensionContent(): DimensionContentInterface; public function addDimensionContent(DimensionContentInterface $dimensionContent): void; diff --git a/Content/Domain/Model/ContentRichEntityTrait.php b/Content/Domain/Model/ContentRichEntityTrait.php index c2f31ed0..3f180e28 100644 --- a/Content/Domain/Model/ContentRichEntityTrait.php +++ b/Content/Domain/Model/ContentRichEntityTrait.php @@ -15,6 +15,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\Criteria; trait ContentRichEntityTrait { @@ -33,6 +34,24 @@ public function getDimensionContents(): Collection return $this->dimensionContents; } + public function findDimensionContent(array $dimensionAttributes): ?DimensionContentInterface + { + $dimensionAttributes = static::getDimensionContentClass()::getEffectiveDimensionAttributes($dimensionAttributes); + + $criteria = Criteria::create(); + foreach ($dimensionAttributes as $key => $value) { + if (null === $value) { + $expr = $criteria->expr()->isNull($key); + } else { + $expr = $criteria->expr()->eq($key, $value); + } + + $criteria->andWhere($expr); + } + + return $this->getDimensionContents()->matching($criteria)->first() ?: null; + } + public function addDimensionContent(DimensionContentInterface $dimensionContent): void { $this->initializeDimensionContents(); diff --git a/Content/Domain/Model/DimensionContentCollection.php b/Content/Domain/Model/DimensionContentCollection.php index a48ebc2f..e7afd8d1 100644 --- a/Content/Domain/Model/DimensionContentCollection.php +++ b/Content/Domain/Model/DimensionContentCollection.php @@ -13,16 +13,13 @@ namespace Sulu\Bundle\ContentBundle\Content\Domain\Model; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Criteria; use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\Merger\MergerInterface; -use Sulu\Component\Util\SortUtils; use Symfony\Component\PropertyAccess\PropertyAccessor; /** * @implements \IteratorAggregate */ -class DimensionContentCollection implements \IteratorAggregate, DimensionContentCollectionInterface +class DimensionContentCollection implements DimensionContentCollectionInterface { /** * @var ContentRichEntityInterface @@ -39,11 +36,6 @@ class DimensionContentCollection implements \IteratorAggregate, DimensionContent */ private $dimensionContentClass; - /** - * @var mixed[] - */ - private $defaultDimensionAttributes; - /** * @var iterable */ @@ -65,52 +57,19 @@ class DimensionContentCollection implements \IteratorAggregate, DimensionContent public function __construct( ContentRichEntityInterface $contentRichEntity, array $dimensionAttributes, - string $dimensionContentClass, iterable $mergers, PropertyAccessor $propertyAccessor ) { $this->contentRichEntity = $contentRichEntity; - $this->dimensionContentClass = $dimensionContentClass; - $this->defaultDimensionAttributes = $dimensionContentClass::getDefaultDimensionAttributes(); - $this->dimensionAttributes = $dimensionContentClass::getEffectiveDimensionAttributes($dimensionAttributes); + $this->dimensionContentClass = $contentRichEntity::getDimensionContentClass(); + $this->dimensionAttributes = $this->dimensionContentClass::getEffectiveDimensionAttributes($dimensionAttributes); $this->mergers = $mergers; $this->propertyAccessor = $propertyAccessor; } - public function getDimensionContentClass(): string - { - return $this->dimensionContentClass; - } - - public function getDimensionContent(array $dimensionAttributes): ?DimensionContentInterface - { - $dimensionAttributes = \array_merge($this->defaultDimensionAttributes, $dimensionAttributes); - - $criteria = Criteria::create(); - foreach ($dimensionAttributes as $key => $value) { - if (null === $value) { - $expr = $criteria->expr()->isNull($key); - } else { - $expr = $criteria->expr()->eq($key, $value); - } - - $criteria->andWhere($expr); - } - - return $this->contentRichEntity->getDimensionContents()->matching($criteria)->first() ?: null; - } - - public function createDimensionContent(array $dimensionAttributes): DimensionContentInterface + public function getResource(): ContentRichEntityInterface { - $dimensionContent = $this->contentRichEntity->createDimensionContent(); - - foreach ($dimensionAttributes as $attributeName => $attributeValue) { - $this->propertyAccessor->setValue($dimensionContent, $attributeName, $attributeValue); - } - - $this->contentRichEntity->addDimensionContent($dimensionContent); - - return $dimensionContent; + return $this->contentRichEntity; } public function getDimensionAttributes(): array @@ -118,27 +77,14 @@ public function getDimensionAttributes(): array return $this->dimensionAttributes; } - public function getIterator() - { - return new ArrayCollection(SortUtils::multisort( - $this->contentRichEntity->getDimensionContents()->toArray(), - \array_keys($this->dimensionAttributes), 'asc' - )); - } - - public function count(): int - { - return $this->contentRichEntity->getDimensionContents()->count(); - } - public function getMergedDimensionContent(): DimensionContentInterface { $unlocalizedDimensionAttributes = $this->dimensionAttributes; $unlocalizedDimensionAttributes['locale'] = null; $dimensionContents = \array_filter([ - $this->getDimensionContent($unlocalizedDimensionAttributes), - $this->getDimensionContent($this->dimensionAttributes), + $this->contentRichEntity->findDimensionContent($unlocalizedDimensionAttributes), + $this->contentRichEntity->findDimensionContent($this->dimensionAttributes), ]); $mergedDimensionContent = null; diff --git a/Content/Domain/Model/DimensionContentCollectionInterface.php b/Content/Domain/Model/DimensionContentCollectionInterface.php index 5caef7a7..bd194b93 100644 --- a/Content/Domain/Model/DimensionContentCollectionInterface.php +++ b/Content/Domain/Model/DimensionContentCollectionInterface.php @@ -16,22 +16,9 @@ /** * @extends \Traversable */ -interface DimensionContentCollectionInterface extends \Traversable, \Countable +interface DimensionContentCollectionInterface extends \Traversable { - /** - * @param mixed[] $dimensionAttributes - */ - public function getDimensionContent(array $dimensionAttributes): ?DimensionContentInterface; - - /** - * @param mixed[] $dimensionAttributes - */ - public function createDimensionContent(array $dimensionAttributes): DimensionContentInterface; - - /** - * @return class-string - */ - public function getDimensionContentClass(): string; + public function getResource(): ContentRichEntityInterface; /** * @return mixed[] diff --git a/Content/Domain/Model/DimensionContentTrait.php b/Content/Domain/Model/DimensionContentTrait.php index e485d22e..647b373e 100644 --- a/Content/Domain/Model/DimensionContentTrait.php +++ b/Content/Domain/Model/DimensionContentTrait.php @@ -60,6 +60,15 @@ public function markAsMerged(): void $this->isMerged = true; } + public function setDimensionAttributes(array $dimensionAttributes): void + { + $dimensionAttributes = static::getEffectiveDimensionAttributes($dimensionAttributes); + + foreach ($dimensionAttributes as $key => $value) { + $this->{$key} = $value; + } + } + public static function getDefaultDimensionAttributes(): array { return [ diff --git a/Resources/config/services.xml b/Resources/config/services.xml index b7daefce..bd1875f7 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -54,7 +54,6 @@ - diff --git a/Tests/Application/ExampleTestBundle/Controller/ExampleController.php b/Tests/Application/ExampleTestBundle/Controller/ExampleController.php index 06b8c65d..7921fb7c 100644 --- a/Tests/Application/ExampleTestBundle/Controller/ExampleController.php +++ b/Tests/Application/ExampleTestBundle/Controller/ExampleController.php @@ -139,7 +139,7 @@ public function postAction(Request $request): Response $data = $this->getData($request); $dimensionAttributes = $this->getDimensionAttributes($request); // ["locale" => "en", "stage" => "draft"] - $dimensionContent = $this->contentManager->persist($example, $data, $dimensionAttributes); + $dimensionContentCollection = $this->contentManager->persist($example, $dimensionAttributes, $data, ); $this->entityManager->persist($example); $this->entityManager->flush(); @@ -233,7 +233,7 @@ public function putAction(Request $request, int $id): Response $dimensionAttributes = $this->getDimensionAttributes($request); // ["locale" => "en", "stage" => "draft"] /** @var ExampleDimensionContent $dimensionContent */ - $dimensionContent = $this->contentManager->persist($example, $data, $dimensionAttributes); + $dimensionContent = $this->contentManager->persist($example, $dimensionAttributes, $data); if (WorkflowInterface::WORKFLOW_PLACE_PUBLISHED === $dimensionContent->getWorkflowPlace()) { $dimensionContent = $this->contentManager->applyTransition( $example, diff --git a/Tests/Application/ExampleTestBundle/Entity/Example.php b/Tests/Application/ExampleTestBundle/Entity/Example.php index 73a842f1..dcfd855e 100644 --- a/Tests/Application/ExampleTestBundle/Entity/Example.php +++ b/Tests/Application/ExampleTestBundle/Entity/Example.php @@ -41,4 +41,9 @@ public function createDimensionContent(): DimensionContentInterface { return new ExampleDimensionContent($this); } + + public static function getDimensionContentClass(): string + { + return ExampleDimensionContent::class; + } } diff --git a/Tests/Unit/Content/Application/ContentCopier/ContentCopierTest.php b/Tests/Unit/Content/Application/ContentCopier/ContentCopierTest.php index e32189e0..4064e26f 100644 --- a/Tests/Unit/Content/Application/ContentCopier/ContentCopierTest.php +++ b/Tests/Unit/Content/Application/ContentCopier/ContentCopierTest.php @@ -63,7 +63,7 @@ public function testCopy(): void ->willReturn(['resolved' => 'data']) ->shouldBeCalled(); - $contentPersister->persist($targetContentRichEntity, ['resolved' => 'data'], $targetDimensionAttributes) + $contentPersister->persist($targetContentRichEntity, $targetDimensionAttributes, ['resolved' => 'data']) ->willReturn($resolvedTargetContent->reveal()) ->shouldBeCalled(); @@ -107,7 +107,7 @@ public function testCopyFromDimensionContentCollection(): void ->willReturn(['resolved' => 'data']) ->shouldBeCalled(); - $contentPersister->persist($targetContentRichEntity, ['resolved' => 'data'], $targetDimensionAttributes) + $contentPersister->persist($targetContentRichEntity, $targetDimensionAttributes, ['resolved' => 'data']) ->willReturn($resolvedTargetContent->reveal()) ->shouldBeCalled(); @@ -145,7 +145,7 @@ public function testCopyFromDimensionContent(): void ->willReturn(['resolved' => 'data']) ->shouldBeCalled(); - $contentPersister->persist($targetContentRichEntity, ['resolved' => 'data'], $targetDimensionAttributes) + $contentPersister->persist($targetContentRichEntity, $targetDimensionAttributes, ['resolved' => 'data']) ->willReturn($resolvedTargetContent->reveal()) ->shouldBeCalled(); diff --git a/Tests/Unit/Content/Application/ContentManager/ContentManagerTest.php b/Tests/Unit/Content/Application/ContentManager/ContentManagerTest.php index eef77a45..6c1976bb 100644 --- a/Tests/Unit/Content/Application/ContentManager/ContentManagerTest.php +++ b/Tests/Unit/Content/Application/ContentManager/ContentManagerTest.php @@ -100,13 +100,13 @@ public function testPersist(): void $contentIndexer->reveal() ); - $contentPersister->persist($contentRichEntity->reveal(), $data, $dimensionAttributes) + $contentPersister->persist($contentRichEntity->reveal(), $dimensionAttributes, $data) ->willReturn($dimensionContent->reveal()) ->shouldBeCalled(); $this->assertSame( $dimensionContent->reveal(), - $contentManager->persist($contentRichEntity->reveal(), $data, $dimensionAttributes) + $contentManager->persist($contentRichEntity->reveal(), $dimensionAttributes, $data) ); } diff --git a/Tests/Unit/Content/Application/ContentPersister/ContentPersisterTest.php b/Tests/Unit/Content/Application/ContentPersister/ContentPersisterTest.php index 5031553f..311b2069 100644 --- a/Tests/Unit/Content/Application/ContentPersister/ContentPersisterTest.php +++ b/Tests/Unit/Content/Application/ContentPersister/ContentPersisterTest.php @@ -77,7 +77,7 @@ public function testPersist(): void $this->assertSame( $mergedDimensionContent->reveal(), - $createContentMessageHandler->persist($example, $data, $attributes) + $createContentMessageHandler->persist($example, $attributes, $data) ); } } diff --git a/Tests/Unit/Content/Domain/Model/DimensionContentCollectionTest.php b/Tests/Unit/Content/Domain/Model/DimensionContentCollectionTest.php index 0b8ef4ea..79182d31 100644 --- a/Tests/Unit/Content/Domain/Model/DimensionContentCollectionTest.php +++ b/Tests/Unit/Content/Domain/Model/DimensionContentCollectionTest.php @@ -32,27 +32,6 @@ protected function createDimensionContentCollectionInstance( return new DimensionContentCollection($dimensionContents, $dimensionAttributes, ExampleDimensionContent::class); } - public function testCount(): void - { - $dimensionContent1 = $this->prophesize(DimensionContentInterface::class); - $dimensionContent1->getLocale()->willReturn(null); - $dimensionContent1->getStage()->willReturn('draft'); - $dimensionContent2 = $this->prophesize(DimensionContentInterface::class); - $dimensionContent2->getLocale()->willReturn('de'); - $dimensionContent2->getStage()->willReturn('draft'); - - $attributes = ['locale' => 'de']; - - $dimensionContentCollection = $this->createDimensionContentCollectionInstance([ - $dimensionContent1->reveal(), - $dimensionContent2->reveal(), - ], $attributes); - - $this->assertCount(2, $dimensionContentCollection); - $this->assertSame(2, \count($dimensionContentCollection)); // @phpstan-ignore-line - $this->assertSame(2, $dimensionContentCollection->count()); // @phpstan-ignore-line - } - public function testSortedByAttributes(): void { $dimensionContent1 = $this->prophesize(DimensionContentInterface::class);