Skip to content

Commit

Permalink
Add ContentLinkProvider (#233)
Browse files Browse the repository at this point in the history
* Add ContentLinkProvider

* Add tests

* Fix namespace

* Trigger ci
  • Loading branch information
Prokyonn committed Feb 9, 2023
1 parent 8435b81 commit b8a51de
Show file tree
Hide file tree
Showing 10 changed files with 552 additions and 88 deletions.
128 changes: 128 additions & 0 deletions Content/Infrastructure/Sulu/Link/ContentLinkProvider.php
@@ -0,0 +1,128 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Link;

use Doctrine\ORM\EntityManagerInterface;
use Sulu\Bundle\ContentBundle\Content\Application\ContentManager\ContentManagerInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\FindContentRichEntitiesTrait;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\ResolveContentDimensionUrlTrait;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\ResolveContentTrait;
use Sulu\Bundle\MarkupBundle\Markup\Link\LinkItem;
use Sulu\Bundle\MarkupBundle\Markup\Link\LinkProviderInterface;
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;

abstract class ContentLinkProvider implements LinkProviderInterface
{
use FindContentRichEntitiesTrait;
use ResolveContentDimensionUrlTrait;
use ResolveContentTrait;

/**
* @var StructureMetadataFactoryInterface
*/
protected $structureMetadataFactory;

/**
* @var ContentManagerInterface
*/
protected $contentManager;

/**
* @var EntityManagerInterface
*/
protected $entityManager;

/**
* @var string
*/
protected $contentRichEntityClass;

public function __construct(
ContentManagerInterface $contentManager,
StructureMetadataFactoryInterface $structureMetadataFactory,
EntityManagerInterface $entityManager,
string $contentRichEntityClass
) {
$this->contentManager = $contentManager;
$this->structureMetadataFactory = $structureMetadataFactory;
$this->entityManager = $entityManager;
$this->contentRichEntityClass = $contentRichEntityClass;
}

public function preload(array $hrefs, $locale, $published = true): array
{
if (0 === \count($hrefs)) {
return [];
}
$items = $this->findEntitiesByIds($hrefs);

return
\array_values(
\array_filter(
\array_map(function(ContentRichEntityInterface $contentRichEntity) use ($locale, $published) {
/** @var DimensionContentInterface|null $resolvedDimensionContent */
$resolvedDimensionContent = $this->resolveContent($contentRichEntity, $locale, !$published);

if (!$resolvedDimensionContent) {
return null;
}

$data = $this->contentManager->normalize($resolvedDimensionContent);

return new LinkItem(
$contentRichEntity->getId(),
(string) $this->getTitle($resolvedDimensionContent, $data),
(string) $this->getUrl($resolvedDimensionContent, $data),
$published
);
}, $items)
)
);
}

/**
* @param mixed[] $data
*/
protected function getTitle(DimensionContentInterface $dimensionContent, array $data): ?string
{
return $data['title'] ?? $data['name'] ?? null;
}

protected function getEntityIdField(): string
{
return 'id';
}

protected function getContentRichEntityClass(): string
{
return $this->contentRichEntityClass;
}

protected function getStructureMetadataFactory(): StructureMetadataFactoryInterface
{
return $this->structureMetadataFactory;
}

protected function getEntityManager(): EntityManagerInterface
{
return $this->entityManager;
}

protected function getContentManager(): ContentManagerInterface
{
return $this->contentManager;
}
}
113 changes: 25 additions & 88 deletions Content/Infrastructure/Sulu/Teaser/ContentTeaserProvider.php
Expand Up @@ -16,19 +16,21 @@
use Doctrine\ORM\EntityManagerInterface;
use Sulu\Bundle\ContentBundle\Content\Application\ContentManager\ContentManagerInterface;
use Sulu\Bundle\ContentBundle\Content\Application\ContentMetadataInspector\ContentMetadataInspectorInterface;
use Sulu\Bundle\ContentBundle\Content\Application\ContentWorkflow\ContentWorkflowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Exception\ContentNotFoundException;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ExcerptInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\TemplateInterface;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\FindContentRichEntitiesTrait;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\ResolveContentDimensionUrlTrait;
use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits\ResolveContentTrait;
use Sulu\Bundle\PageBundle\Teaser\Provider\TeaserProviderInterface;
use Sulu\Bundle\PageBundle\Teaser\Teaser;
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;

abstract class ContentTeaserProvider implements TeaserProviderInterface
{
public const CONTENT_RICH_ENTITY_ALIAS = ContentWorkflowInterface::CONTENT_RICH_ENTITY_CONTEXT_KEY;
use FindContentRichEntitiesTrait;
use ResolveContentDimensionUrlTrait;
use ResolveContentTrait;

/**
* @var ContentManagerInterface
Expand Down Expand Up @@ -150,58 +152,6 @@ protected function createTeaser(DimensionContentInterface $dimensionContent, arr
);
}

protected function resolveContent(ContentRichEntityInterface $contentRichEntity, string $locale): ?DimensionContentInterface
{
$stage = $this->showDrafts
// TODO FIXME add testcase for it
? DimensionContentInterface::STAGE_DRAFT // @codeCoverageIgnore
: DimensionContentInterface::STAGE_LIVE;

try {
$resolvedDimensionContent = $this->contentManager->resolve($contentRichEntity, [
'locale' => $locale,
'stage' => $stage,
]);
} catch (ContentNotFoundException $exception) {
return null;
}

if ($stage !== $resolvedDimensionContent->getStage() || $locale !== $resolvedDimensionContent->getLocale()) {
return null;
}

return $resolvedDimensionContent;
}

/**
* @param mixed[] $data
*/
protected function getUrl(DimensionContentInterface $dimensionContent, array $data): ?string
{
if (!$dimensionContent instanceof TemplateInterface) {
// TODO FIXME add testcase for it
return null; // @codeCoverageIgnore
}

$type = $dimensionContent::getTemplateType();
$template = $dimensionContent->getTemplateKey();

$metadata = $this->metadataFactory->getStructureMetadata($type, $template);

if (!$metadata) {
// TODO FIXME add testcase for it
return null; // @codeCoverageIgnore
}

foreach ($metadata->getProperties() as $property) {
if ('route' === $property->getType()) {
return $dimensionContent->getTemplateData()[$property->getName()] ?? null;
}
}

return null;
}

/**
* @param mixed[] $data
*/
Expand Down Expand Up @@ -269,48 +219,35 @@ protected function getAttributes(DimensionContentInterface $dimensionContent, ar
return [];
}

/**
* @param mixed[] $ids
*
* @return ContentRichEntityInterface[]
*/
protected function findEntitiesByIds(array $ids): array
protected function getResourceKey(): string
{
$entityIdField = $this->getEntityIdField();
$classMetadata = $this->entityManager->getClassMetadata($this->contentRichEntityClass);

$entities = $this->entityManager->createQueryBuilder()
->select(self::CONTENT_RICH_ENTITY_ALIAS)
->from($this->contentRichEntityClass, self::CONTENT_RICH_ENTITY_ALIAS)
->where(self::CONTENT_RICH_ENTITY_ALIAS . '.' . $entityIdField . ' IN (:ids)')
->getQuery()
->setParameter('ids', $ids)
->getResult();

$idPositions = \array_flip($ids);

\usort(
$entities,
function(ContentRichEntityInterface $a, ContentRichEntityInterface $b) use ($idPositions, $classMetadata, $entityIdField) {
$aId = $classMetadata->getIdentifierValues($a)[$entityIdField];
$bId = $classMetadata->getIdentifierValues($b)[$entityIdField];

return $idPositions[$aId] - $idPositions[$bId];
}
);
$dimensionContentClass = $this->contentMetadataInspector->getDimensionContentClass($this->contentRichEntityClass);

return $entities;
return $dimensionContentClass::getResourceKey();
}

protected function getEntityIdField(): string
{
return 'id';
}

protected function getResourceKey(): string
protected function getContentRichEntityClass(): string
{
$dimensionContentClass = $this->contentMetadataInspector->getDimensionContentClass($this->contentRichEntityClass);
return $this->contentRichEntityClass;
}

return $dimensionContentClass::getResourceKey();
protected function getEntityManager(): EntityManagerInterface
{
return $this->entityManager;
}

protected function getStructureMetadataFactory(): StructureMetadataFactoryInterface
{
return $this->metadataFactory;
}

protected function getContentManager(): ContentManagerInterface
{
return $this->contentManager;
}
}
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits;

use Doctrine\ORM\EntityManagerInterface;
use Sulu\Bundle\ContentBundle\Content\Application\ContentWorkflow\ContentWorkflowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface;

trait FindContentRichEntitiesTrait
{
/**
* @param string[]|int[] $ids
*
* @return ContentRichEntityInterface[]
*/
protected function findEntitiesByIds(array $ids): array
{
$entityIdField = $this->getEntityIdField();
$entityManager = $this->getEntityManager();
$contentRichEntityClass = $this->getContentRichEntityClass();
$classMetadata = $entityManager->getClassMetadata($contentRichEntityClass);

$entities = $entityManager->createQueryBuilder()
->select(ContentWorkflowInterface::CONTENT_RICH_ENTITY_CONTEXT_KEY)
->from($contentRichEntityClass, ContentWorkflowInterface::CONTENT_RICH_ENTITY_CONTEXT_KEY)
->where(ContentWorkflowInterface::CONTENT_RICH_ENTITY_CONTEXT_KEY . '.' . $entityIdField . ' IN (:ids)')
->getQuery()
->setParameter('ids', $ids)
->getResult();

$idPositions = \array_flip($ids);

\usort(
$entities,
function(ContentRichEntityInterface $a, ContentRichEntityInterface $b) use ($idPositions, $classMetadata, $entityIdField) {
$aId = $classMetadata->getIdentifierValues($a)[$entityIdField];
$bId = $classMetadata->getIdentifierValues($b)[$entityIdField];

return $idPositions[$aId] - $idPositions[$bId];
}
);

return $entities;
}

abstract protected function getEntityIdField(): string;

abstract protected function getContentRichEntityClass(): string;

abstract protected function getEntityManager(): EntityManagerInterface;
}
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Traits;

use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\TemplateInterface;
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;

trait ResolveContentDimensionUrlTrait
{
/**
* @param mixed[] $data
*/
protected function getUrl(DimensionContentInterface $dimensionContent, array $data): ?string
{
if (!$dimensionContent instanceof TemplateInterface) {
// TODO FIXME add testcase for it
return null; // @codeCoverageIgnore
}

$type = $dimensionContent::getTemplateType();
$template = $dimensionContent->getTemplateKey();

$metadata = $this->getStructureMetadataFactory()->getStructureMetadata($type, $template);

if (!$metadata) {
// TODO FIXME add testcase for it
return null; // @codeCoverageIgnore
}

foreach ($metadata->getProperties() as $property) {
if ('route' === $property->getType()) {
return $dimensionContent->getTemplateData()[$property->getName()] ?? null;
}
}

return null;
}

abstract protected function getStructureMetadataFactory(): StructureMetadataFactoryInterface;
}

0 comments on commit b8a51de

Please sign in to comment.