From d88794f03bb05bf82b4358c49bff28f090916048 Mon Sep 17 00:00:00 2001 From: Prokyonn Date: Thu, 14 Nov 2024 13:42:46 +0100 Subject: [PATCH] Replace ContentBundleLinkProvider with own ArticleLinkProvider (#702) --- .../Doctrine/Repository/ArticleRepository.php | 7 ++- .../Sulu/Content/ArticleDataProvider.php | 49 ++++++++++++--- .../Sulu/Content/ArticleLinkProvider.php | 63 ++++++++++++++----- .../Symfony/HttpKernel/SuluArticleBundle.php | 7 ++- tests/Traits/AssertSnapshotTrait.php | 2 +- 5 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/Infrastructure/Doctrine/Repository/ArticleRepository.php b/src/Infrastructure/Doctrine/Repository/ArticleRepository.php index 9006b345..e0d89ce8 100644 --- a/src/Infrastructure/Doctrine/Repository/ArticleRepository.php +++ b/src/Infrastructure/Doctrine/Repository/ArticleRepository.php @@ -157,8 +157,10 @@ public function findIdentifiersBy(array $filters = [], array $sortBy = []): iter $queryBuilder->select('DISTINCT article.uuid'); // we need to select the fields which are used in the order by clause - /** @var OrderBy $orderBy */ - foreach ($queryBuilder->getDQLPart('orderBy') as $orderBy) { + + /** @var OrderBy[] $orderBys */ + $orderBys = $queryBuilder->getDQLPart('orderBy'); + foreach ($orderBys as $orderBy) { $queryBuilder->addSelect(\explode(' ', $orderBy->getParts()[0])[0]); } @@ -198,6 +200,7 @@ public function remove(ArticleInterface $article): void * @param array{ * uuid?: 'asc'|'desc', * title?: 'asc'|'desc', + * created?: 'asc'|'desc', * } $sortBy * @param array{ * article_admin?: bool, diff --git a/src/Infrastructure/Sulu/Content/ArticleDataProvider.php b/src/Infrastructure/Sulu/Content/ArticleDataProvider.php index d564ec8f..b3719908 100644 --- a/src/Infrastructure/Sulu/Content/ArticleDataProvider.php +++ b/src/Infrastructure/Sulu/Content/ArticleDataProvider.php @@ -22,6 +22,7 @@ use Sulu\Component\SmartContent\DataProviderAliasInterface; use Sulu\Component\SmartContent\DataProviderInterface; use Sulu\Component\SmartContent\DataProviderResult; +use Sulu\Component\SmartContent\DatasourceItemInterface; class ArticleDataProvider implements DataProviderInterface, DataProviderAliasInterface { @@ -65,20 +66,23 @@ protected function getConfigurationBuilder(): BuilderInterface public function getDefaultPropertyParameter(): array { return [ - 'type' => new PropertyParameter('type', null), + 'type' => new PropertyParameter('type', ''), 'ignoreWebspaces' => new PropertyParameter('ignoreWebspaces', false), ]; } public function resolveDataItems(array $filters, array $propertyParameter, array $options = [], $limit = null, $page = 1, $pageSize = null) { - [$filters, $sortBy] = $this->resolveFilters($filters, $propertyParameter, $page, $options['locale']); + /** @var string $locale */ + $locale = $options['locale']; + [$filters, $sortBy] = $this->resolveFilters($filters, $propertyParameter, $page, $locale); $dimensionAttributes = [ - 'locale' => $options['locale'], + 'locale' => $locale, 'stage' => $this->showDrafts ? DimensionContentInterface::STAGE_DRAFT : DimensionContentInterface::STAGE_LIVE, ]; + /** @var string[] $identifiers */ $identifiers = $this->articleRepository->findIdentifiersBy( filters: \array_merge($dimensionAttributes, $filters), sortBy: $sortBy @@ -105,13 +109,16 @@ public function resolveDataItems(array $filters, array $propertyParameter, array public function resolveResourceItems(array $filters, array $propertyParameter, array $options = [], $limit = null, $page = 1, $pageSize = null): DataProviderResult { - [$filters, $sortBy] = $this->resolveFilters($filters, $propertyParameter, $page, $options['locale']); + /** @var string $locale */ + $locale = $options['locale']; + [$filters, $sortBy] = $this->resolveFilters($filters, $propertyParameter, $page, $locale); $dimensionAttributes = [ - 'locale' => $options['locale'], + 'locale' => $locale, 'stage' => $this->showDrafts ? DimensionContentInterface::STAGE_DRAFT : DimensionContentInterface::STAGE_LIVE, ]; + /** @var string[] $identifiers */ $identifiers = $this->articleRepository->findIdentifiersBy( filters: \array_merge($dimensionAttributes, $filters), sortBy: $sortBy @@ -127,6 +134,7 @@ public function resolveResourceItems(array $filters, array $propertyParameter, a foreach ($articles as $article) { $dimensionContent = $this->contentManager->resolve($article, $dimensionAttributes); $result[] = $this->contentManager->normalize($dimensionContent); + $this->articleReferenceStore->add($article->getId()); } $hasNextPage = \count($result) > ($pageSize ?? $limit); @@ -135,7 +143,30 @@ public function resolveResourceItems(array $filters, array $propertyParameter, a } /** + * @param array{ + * categories?: int[], + * categoryOperator?: 'or'|'and', + * tags?: string[], + * tagOperator?: 'or'|'and', + * limitResult?: int, + * sortBy?: string, + * sortMethod?: 'asc'|'desc', + * ... + * } $filters * @param array $propertyParameter + * + * @return array{ + * array{ + * locale: string, + * categoryIds?: int[], + * categoryOperator?: 'AND'|'OR', + * tagIds?: int[], + * tagOperator?: 'AND'|'OR', + * limit?: int, + * page: int + * }, + * array + * } */ protected function resolveFilters( array $filters, array $propertyParameter, int $page, string $locale): array @@ -148,13 +179,13 @@ protected function resolveFilters( $filter['categoryIds'] = $filters['categories']; } if (isset($filters['categoryOperator'])) { - $filter['categoryOperator'] = $filters['categoryOperator']; + $filter['categoryOperator'] = \strtoupper($filters['categoryOperator']); } if (isset($filters['tags'])) { $filter['tagIds'] = $filters['tags']; } if (isset($filters['tagOperator'])) { - $filter['tagOperator'] = $filters['tagOperator']; + $filter['tagOperator'] = \strtoupper($filters['tagOperator']); } if (isset($filters['limitResult']) || isset($propertyParameter['max_per_page'])) { $filter['limit'] = (int) ($filters['limitResult'] ?? $propertyParameter['max_per_page']->getValue()); @@ -168,9 +199,9 @@ protected function resolveFilters( return [$filter, $sortBy]; } - public function resolveDatasource($datasource, array $propertyParameter, array $options): void + public function resolveDatasource($datasource, array $propertyParameter, array $options): ?DatasourceItemInterface { - return; + return null; } public function getAlias() diff --git a/src/Infrastructure/Sulu/Content/ArticleLinkProvider.php b/src/Infrastructure/Sulu/Content/ArticleLinkProvider.php index 53a9fcca..51a72c7c 100644 --- a/src/Infrastructure/Sulu/Content/ArticleLinkProvider.php +++ b/src/Infrastructure/Sulu/Content/ArticleLinkProvider.php @@ -13,43 +13,72 @@ namespace Sulu\Article\Infrastructure\Sulu\Content; -use Doctrine\ORM\EntityManagerInterface; -use Sulu\Article\Domain\Model\ArticleDimensionContentInterface; use Sulu\Article\Domain\Model\ArticleInterface; +use Sulu\Article\Domain\Repository\ArticleRepositoryInterface; use Sulu\Bundle\ContentBundle\Content\Application\ContentManager\ContentManagerInterface; -use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Link\ContentLinkProvider; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; use Sulu\Bundle\MarkupBundle\Markup\Link\LinkConfiguration; use Sulu\Bundle\MarkupBundle\Markup\Link\LinkConfigurationBuilder; -use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface; +use Sulu\Bundle\MarkupBundle\Markup\Link\LinkItem; +use Sulu\Bundle\MarkupBundle\Markup\Link\LinkProviderInterface; +use Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStoreInterface; +use Symfony\Contracts\Translation\TranslatorInterface; -/** - * @extends ContentLinkProvider - */ -class ArticleLinkProvider extends ContentLinkProvider +class ArticleLinkProvider implements LinkProviderInterface { public function __construct( - ContentManagerInterface $contentManager, - StructureMetadataFactoryInterface $structureMetadataFactory, - EntityManagerInterface $entityManager, + private readonly ContentManagerInterface $contentManager, + private readonly ArticleRepositoryInterface $articleRepository, + private readonly ReferenceStoreInterface $articleReferenceStore, + private readonly TranslatorInterface $translator, ) { - parent::__construct($contentManager, $structureMetadataFactory, $entityManager, ArticleInterface::class); } public function getConfiguration(): LinkConfiguration { return LinkConfigurationBuilder::create() - ->setTitle('Example') + ->setTitle($this->translator->trans('sulu_article.articles', [], 'admin')) ->setResourceKey(ArticleInterface::RESOURCE_KEY) ->setListAdapter('table') ->setDisplayProperties(['id']) - ->setOverlayTitle('Select Example') - ->setEmptyText('No example selected') + ->setOverlayTitle($this->translator->trans('sulu_article.selection_overlay_title', [], 'admin')) + ->setEmptyText($this->translator->trans('sulu_article.no_article_selected', [], 'admin')) ->setIcon('su-document') ->getLinkConfiguration(); } - protected function getEntityIdField(): string + public function preload(array $hrefs, $locale, $published = true) { - return 'uuid'; + $dimensionAttributes = [ + 'locale' => $locale, + 'stage' => $published ? DimensionContentInterface::STAGE_LIVE : DimensionContentInterface::STAGE_DRAFT, + ]; + + $articles = $this->articleRepository->findBy( + filters: [...$dimensionAttributes, 'uuids' => $hrefs], + selects: [ArticleRepositoryInterface::GROUP_SELECT_ARTICLE_WEBSITE => true] + ); + + $result = []; + foreach ($articles as $article) { + $dimensionContent = $this->contentManager->resolve($article, $dimensionAttributes); + $this->articleReferenceStore->add($article->getId()); + + /** @var string|null $url */ + $url = $dimensionContent->getTemplateData()['url'] ?? null; + if (null === $url) { + // TODO what to do when there is no url? + continue; + } + + $result[] = new LinkItem( + $article->getUuid(), + (string) $dimensionContent->getTitle(), + $url, + $published + ); + } + + return $result; } } diff --git a/src/Infrastructure/Symfony/HttpKernel/SuluArticleBundle.php b/src/Infrastructure/Symfony/HttpKernel/SuluArticleBundle.php index fe66e394..ec5a669f 100644 --- a/src/Infrastructure/Symfony/HttpKernel/SuluArticleBundle.php +++ b/src/Infrastructure/Symfony/HttpKernel/SuluArticleBundle.php @@ -227,9 +227,10 @@ public function loadExtension(array $config, ContainerConfigurator $container, C $services->set('sulu_article.article_link_provider') ->class(ArticleLinkProvider::class) ->args([ - new Reference('sulu_content.content_manager'), // TODO link provider should not build on manager - new Reference('sulu_page.structure.factory'), - new Reference('doctrine.orm.entity_manager'), + new Reference('sulu_content.content_manager'), + new Reference('sulu_article.article_repository'), + new Reference('sulu_article.article_reference_store'), + new Reference('translator'), ]) ->tag('sulu.link.provider', ['alias' => 'article']); diff --git a/tests/Traits/AssertSnapshotTrait.php b/tests/Traits/AssertSnapshotTrait.php index d14d72d5..35ba8e4d 100644 --- a/tests/Traits/AssertSnapshotTrait.php +++ b/tests/Traits/AssertSnapshotTrait.php @@ -25,7 +25,7 @@ trait AssertSnapshotTrait */ protected function assertResponseSnapshot( string $snapshotPatternFilename, - $actualResponse, + $actualResponse, int $statusCode = 200, string $message = '' ): void {