From e8c8b7b2c36c42c9f30092c20508d0f87a65fb2b Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 6 Oct 2021 18:25:18 +0200 Subject: [PATCH] Refractor dimension content collection behaviour --- .../ContentDataMapper/ContentDataMapper.php | 16 ++- .../ContentDataMapperInterface.php | 4 +- .../ContentPersister/ContentPersister.php | 26 ++--- .../DimensionContentCollectionFactory.php | 98 ++++--------------- ...nsionContentCollectionFactoryInterface.php | 3 +- .../Model/ContentRichEntityInterface.php | 1 + .../Model/DimensionContentCollection.php | 94 +++++++++++++++--- .../DimensionContentCollectionInterface.php | 7 ++ 8 files changed, 136 insertions(+), 113 deletions(-) diff --git a/Content/Application/ContentDataMapper/ContentDataMapper.php b/Content/Application/ContentDataMapper/ContentDataMapper.php index 81e29d3d..04d4105d 100644 --- a/Content/Application/ContentDataMapper/ContentDataMapper.php +++ b/Content/Application/ContentDataMapper/ContentDataMapper.php @@ -32,11 +32,21 @@ public function __construct(iterable $dataMappers) } public function map( - array $data, - DimensionContentCollectionInterface $dimensionContentCollection + DimensionContentCollectionInterface $dimensionContentCollection, + array $data ): void { + $localizedDimensionAttributes = $dimensionContentCollection->getDimensionAttributes(); + $unlocalizedDimensionAttributes = $localizedDimensionAttributes; + $unlocalizedDimensionAttributes['locale'] = null; + + $unlocalizedDimensionContent = $dimensionContentCollection->getDimensionContent($unlocalizedDimensionAttributes) + ?: $dimensionContentCollection->createDimensionContent($unlocalizedDimensionAttributes); + + $localizedDimensionContent = $dimensionContentCollection->getDimensionContent($localizedDimensionAttributes) + ?: $dimensionContentCollection->createDimensionContent($localizedDimensionAttributes); + foreach ($this->dataMappers as $mapper) { - $mapper->map($data, $dimensionContentCollection); + $mapper->map($data, $unlocalizedDimensionContent, $localizedDimensionContent); } } } diff --git a/Content/Application/ContentDataMapper/ContentDataMapperInterface.php b/Content/Application/ContentDataMapper/ContentDataMapperInterface.php index 4f7db9c7..74e913e0 100644 --- a/Content/Application/ContentDataMapper/ContentDataMapperInterface.php +++ b/Content/Application/ContentDataMapper/ContentDataMapperInterface.php @@ -21,7 +21,7 @@ interface ContentDataMapperInterface * @param array $data */ public function map( - array $data, - DimensionContentCollectionInterface $dimensionContentCollection + DimensionContentCollectionInterface $dimensionContentCollection, + array $data ): void; } diff --git a/Content/Application/ContentPersister/ContentPersister.php b/Content/Application/ContentPersister/ContentPersister.php index c06317aa..9c10f8db 100644 --- a/Content/Application/ContentPersister/ContentPersister.php +++ b/Content/Application/ContentPersister/ContentPersister.php @@ -13,9 +13,11 @@ namespace Sulu\Bundle\ContentBundle\Content\Application\ContentPersister; +use Sulu\Bundle\ContentBundle\Content\Application\ContentDataMapper\ContentDataMapperInterface; use Sulu\Bundle\ContentBundle\Content\Application\ContentMerger\ContentMergerInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Factory\DimensionContentCollectionFactoryInterface; 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 ContentPersister implements ContentPersisterInterface @@ -25,6 +27,11 @@ class ContentPersister implements ContentPersisterInterface */ private $dimensionContentCollectionFactory; + /** + * @var ContentDataMapperInterface + */ + private $contentDataMapper; + /** * @var ContentMergerInterface */ @@ -32,28 +39,23 @@ class ContentPersister implements ContentPersisterInterface public function __construct( DimensionContentCollectionFactoryInterface $dimensionContentCollectionFactory, + ContentDataMapperInterface $contentDataMapper, ContentMergerInterface $contentMerger ) { $this->dimensionContentCollectionFactory = $dimensionContentCollectionFactory; + $this->contentDataMapper = $contentDataMapper; $this->contentMerger = $contentMerger; } - public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): DimensionContentInterface + public function persist(ContentRichEntityInterface $contentRichEntity, array $data, array $dimensionAttributes): 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, $data); + + return $dimensionContentCollection; } } diff --git a/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php b/Content/Application/DimensionContentCollectionFactory/DimensionContentCollectionFactory.php index d9dd7f81..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($data, $dimensionContentCollection); - - 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; }