Skip to content

Commit

Permalink
Add versioning to dimension content
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-schranz committed Oct 2, 2021
1 parent d9497bd commit 651aa2a
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 20 deletions.
8 changes: 5 additions & 3 deletions Content/Application/ContentWorkflow/ContentWorkflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,11 @@ private function getWorkflow(): SymfonyWorkflowInterface
// | New |--------->| Unpublished | | Review |---------->| Published | | draft | | Review draft |
// | | | |<---------------------| | | |--------------->| |<----------------------------| |
// +-----+ +-------------+ reject +--------+ +------------+ create draft +-------+ reject draft +---------------+
// A | A |
// +---+ | publish |
// publish +----------------------------------------------------------------+
// A | A | A | A |
// +---+ +---+ | | restore | |
// restore publish | +----------------------+ |
// | publish |
// +--------------------------------------------------------------------+

// Configures places
$definition = $definitionBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\WorkflowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Repository\DimensionContentRepositoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\TransitionEvent;

Expand All @@ -29,9 +30,17 @@ class PublishTransitionSubscriber implements EventSubscriberInterface
*/
private $contentCopier;

public function __construct(ContentCopierInterface $contentCopier)
{
/**
* @var DimensionContentRepositoryInterface
*/
private $dimensionContentRepository;

public function __construct(
ContentCopierInterface $contentCopier,
DimensionContentRepositoryInterface $dimensionContentRepository
) {
$this->contentCopier = $contentCopier;
$this->dimensionContentRepository = $dimensionContentRepository;
}

public function onPublish(TransitionEvent $transitionEvent): void
Expand Down Expand Up @@ -68,6 +77,21 @@ public function onPublish(TransitionEvent $transitionEvent): void

$dimensionAttributes['stage'] = DimensionContentInterface::STAGE_LIVE;

// create new version
// TODO optimize latest version and publish locales on write process to avoid loading them here?
$version = 1 + $this->dimensionContentRepository->getLatestVersion($contentRichEntity);
$publishLocales = $this->dimensionContentRepository->getLocales($contentRichEntity, $dimensionAttributes);

foreach ($publishLocales as $publishLocale) {
$this->contentCopier->copy(
$contentRichEntity,
\array_merge($dimensionAttributes, ['locale' => $publishLocale]),
$contentRichEntity,
\array_merge($dimensionAttributes, ['locale' => $publishLocale, 'version' => $version])
);
}

// publish content into live workspace
$this->contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection,
$contentRichEntity,
Expand Down
6 changes: 6 additions & 0 deletions Content/Domain/Model/DimensionContentInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface DimensionContentInterface
public const STAGE_DRAFT = 'draft';
public const STAGE_LIVE = 'live';

public const DEFAULT_VERSION = 0;

public static function getResourceKey(): string;

public function getLocale(): ?string;
Expand All @@ -28,6 +30,10 @@ public function getStage(): string;

public function setStage(string $stage): void;

public function getVersion(): int;

public function setVersion(int $version): void;

public function getResource(): ContentRichEntityInterface;

public function isMerged(): bool;
Expand Down
16 changes: 16 additions & 0 deletions Content/Domain/Model/DimensionContentTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ trait DimensionContentTrait
*/
protected $stage = DimensionContentInterface::STAGE_DRAFT;

/**
* @var int
*/
protected $version = DimensionContentInterface::DEFAULT_VERSION;

/**
* @var bool
*/
Expand All @@ -50,6 +55,16 @@ public function getStage(): string
return $this->stage;
}

public function setVersion(int $version): void
{
$this->version = $version;
}

public function getVersion(): int
{
return $this->version;
}

public function isMerged(): bool
{
return $this->isMerged;
Expand All @@ -65,6 +80,7 @@ public static function getDefaultDimensionAttributes(): array
return [
'locale' => null,
'stage' => DimensionContentInterface::STAGE_DRAFT,
'version' => DimensionContentInterface::DEFAULT_VERSION,
];
}

Expand Down
12 changes: 12 additions & 0 deletions Content/Domain/Repository/DimensionContentRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,16 @@ public function load(
ContentRichEntityInterface $contentRichEntity,
array $dimensionAttributes
): DimensionContentCollectionInterface;

public function getLatestVersion(ContentRichEntityInterface $contentRichEntity): int;

/**
* @param mixed[] $dimensionAttributes
*
* @return string[]
*/
public function getLocales(
ContentRichEntityInterface $contentRichEntity,
array $dimensionAttributes
): array;
}
39 changes: 39 additions & 0 deletions Content/Infrastructure/Doctrine/DimensionContentRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,43 @@ public function load(
$dimensionContentClass
);
}

public function getLatestVersion(ContentRichEntityInterface $contentRichEntity): int
{
$dimensionContentClass = $this->contentMetadataInspector->getDimensionContentClass(\get_class($contentRichEntity));

$queryBuilder = $this->entityManager->createQueryBuilder()
->from($dimensionContentClass, 'dimensionContent')
->select('dimensionContent.version')
->orderBy('dimensionContent.version', 'DESC')
->setMaxResults(1)
->where('content.id = :id')
->setParameter('id', $contentRichEntity->getId());

return (int) $queryBuilder->getQuery()->getSingleScalarResult();
}

public function getLocales(
ContentRichEntityInterface $contentRichEntity,
array $dimensionAttributes
): array {
$dimensionContentClass = $this->contentMetadataInspector->getDimensionContentClass(\get_class($contentRichEntity));

$queryBuilder = $this->entityManager->createQueryBuilder()
->from($dimensionContentClass, 'dimensionContent')
->select('dimensionContent.locale')
->where('content.id = :id')
->andWhere('dimensionContent.locale IS NOT NULL')
->setParameter('id', $contentRichEntity->getId());

unset($dimensionAttributes['locale']);
foreach ($dimensionAttributes as $key => $value) {
$queryBuilder->andWhere('dimensionContent.' . $key, ':' . $key)
->setParameter(':' . $key, $value);
}

return \array_map(function($row) {
return $row['locale'];
}, $queryBuilder->getQuery()->getArrayResult());
}
}
1 change: 1 addition & 0 deletions Content/Infrastructure/Doctrine/MetadataLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $event): void
if ($reflection->implementsInterface(DimensionContentInterface::class)) {
$this->addField($metadata, 'stage', 'string', ['length' => 16, 'nullable' => false]);
$this->addField($metadata, 'locale', 'string', ['length' => 7, 'nullable' => true]);
$this->addField($metadata, 'version', 'integer', ['nullable' => false, 'default' => 0]);
}

if ($reflection->implementsInterface(SeoInterface::class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,30 @@
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\WorkflowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Repository\DimensionContentRepositoryInterface;
use Symfony\Component\Workflow\Event\TransitionEvent;
use Symfony\Component\Workflow\Marking;

class PublishTransitionSubscriberTest extends TestCase
{
public function createContentPublisherSubscriberInstance(
ContentCopierInterface $contentCopier
ContentCopierInterface $contentCopier,
DimensionContentRepositoryInterface $dimensionContentRepository
): PublishTransitionSubscriber {
return new PublishTransitionSubscriber($contentCopier);
return new PublishTransitionSubscriber(
$contentCopier,
$dimensionContentRepository
);
}

public function testGetSubscribedEvents(): void
{
$contentCopier = $this->prophesize(ContentCopierInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$this->assertSame([
'workflow.content_workflow.transition.publish' => 'onPublish',
Expand All @@ -54,7 +63,11 @@ public function testOnPublishNoDimensionContentInterface(): void
$contentCopier = $this->prophesize(ContentCopierInterface::class);
$contentCopier->copyFromDimensionContentCollection(Argument::cetera())->shouldNotBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand All @@ -81,7 +94,11 @@ public function testOnPublishNoDimensionContentCollection(): void
$contentCopier->copyFromDimensionContentCollection(Argument::any(), Argument::any(), Argument::any())
->shouldNotBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand All @@ -108,7 +125,11 @@ public function testOnPublishNoContentRichEntity(): void
$contentCopier->copyFromDimensionContentCollection(Argument::any(), Argument::any(), Argument::any())
->shouldNotBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand All @@ -135,7 +156,11 @@ public function testOnPublishNoDimensionAttributes(): void
$contentCopier->copyFromDimensionContentCollection(Argument::any(), Argument::any(), Argument::any())
->shouldNotBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand All @@ -162,19 +187,30 @@ public function testOnPublish(): void
]);

$contentCopier = $this->prophesize(ContentCopierInterface::class);
$sourceDimensionAttributes = $dimensionAttributes;
$sourceDimensionAttributes['stage'] = 'live';
$targetDimensionAttributes = $dimensionAttributes;
$targetDimensionAttributes['stage'] = 'live';

$resolvedCopiedContent = $this->prophesize(DimensionContentInterface::class);
$contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection->reveal(),
$contentRichEntity->reveal(),
$sourceDimensionAttributes
$targetDimensionAttributes
)
->willReturn($resolvedCopiedContent->reveal())
->shouldBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$dimensionContentRepository->getLatestVersion($contentRichEntity->reveal())
->willReturn(0)
->shouldBeCalled();
$dimensionContentRepository->getLocales($contentRichEntity->reveal(), $targetDimensionAttributes)
->willReturn([])
->shouldBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand All @@ -201,19 +237,44 @@ public function testOnPublishExistingPublished(): void
]);

$contentCopier = $this->prophesize(ContentCopierInterface::class);
$sourceDimensionAttributes = $dimensionAttributes;
$sourceDimensionAttributes['stage'] = 'live';
$targetDimensionAttributes = $dimensionAttributes;
$targetDimensionAttributes['stage'] = 'live';

$resolvedCopiedContent = $this->prophesize(DimensionContentInterface::class);
$contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection->reveal(),
$contentRichEntity->reveal(),
$sourceDimensionAttributes
$targetDimensionAttributes
)
->willReturn($resolvedCopiedContent->reveal())
->shouldBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance($contentCopier->reveal());
$dimensionContentRepository = $this->prophesize(DimensionContentRepositoryInterface::class);
$dimensionContentRepository->getLatestVersion($contentRichEntity->reveal())
->willReturn(0)
->shouldBeCalled();
$dimensionContentRepository->getLocales($contentRichEntity->reveal(), $targetDimensionAttributes)
->willReturn(['en', 'de'])
->shouldBeCalled();

$contentCopier->copy(
$contentRichEntity->reveal(),
\array_merge($targetDimensionAttributes, ['locale' => 'en']),
$contentRichEntity->reveal(),
\array_merge($targetDimensionAttributes, ['locale' => 'en', 'version' => 1])
)->shouldBeCalled();

$contentCopier->copy(
$contentRichEntity->reveal(),
\array_merge($targetDimensionAttributes, ['locale' => 'de']),
$contentRichEntity->reveal(),
\array_merge($targetDimensionAttributes, ['locale' => 'de', 'version' => 1])
)->shouldBeCalled();

$contentPublishSubscriber = $this->createContentPublisherSubscriberInstance(
$contentCopier->reveal(),
$dimensionContentRepository->reveal()
);

$contentPublishSubscriber->onPublish($event);
}
Expand Down

0 comments on commit 651aa2a

Please sign in to comment.