diff --git a/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancer.php b/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancer.php index 6b6f310f..f241371e 100644 --- a/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancer.php +++ b/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancer.php @@ -79,12 +79,18 @@ class DimensionContentQueryEnhancer * templateKeys?: string[], * loadGhost?: bool, * } $filters + * @param array{ + * title?: 'asc'|'desc', + * authored?: 'asc'|'desc', + * workflowPublished?: 'asc'|'desc', + * } $sortBys */ public function addFilters( QueryBuilder $queryBuilder, string $contentRichEntityAlias, string $dimensionContentClassName, - array $filters + array $filters, + array $sortBys ): void { $effectiveAttributes = $dimensionContentClassName::getEffectiveDimensionAttributes($filters); @@ -92,7 +98,7 @@ public function addFilters( $dimensionContentClassName, 'filterDimensionContent', Join::WITH, - 'filterDimensionContent.' . $contentRichEntityAlias . ' = ' . $contentRichEntityAlias . '' + 'filterDimensionContent.' . $contentRichEntityAlias . ' = ' . $contentRichEntityAlias ); foreach ($effectiveAttributes as $key => $value) { @@ -182,6 +188,15 @@ public function addFilters( ->setParameter('templateKeys', $templateKeys); } } + + // Sort by + foreach ($sortBys as $field => $order) { + if (!\in_array($field, ['title', 'authored', 'workflowPublished'], true)) { + continue; + } + + $queryBuilder->addOrderBy('filterDimensionContent.' . $field, $order); + } } /** diff --git a/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php b/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php index 220c1151..795ccd0b 100644 --- a/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php +++ b/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php @@ -207,7 +207,7 @@ public function countBy(array $filters = []): int * @param array{ * id?: 'asc'|'desc', * title?: 'asc'|'desc', - * } $sortBy + * } $sortBys * @param array{ * example_admin?: bool, * example_website?: bool, @@ -216,9 +216,9 @@ public function countBy(array $filters = []): int * * @return \Generator */ - public function findBy(array $filters = [], array $sortBy = [], array $selects = []): \Generator + public function findBy(array $filters = [], array $sortBys = [], array $selects = []): \Generator { - $queryBuilder = $this->createQueryBuilder($filters, $sortBy, $selects); + $queryBuilder = $this->createQueryBuilder($filters, $sortBys, $selects); // TODO optimize hydration with toIterable() /** @var iterable $examples */ @@ -258,14 +258,14 @@ public function remove(Example $example): void * @param array{ * id?: 'asc'|'desc', * title?: 'asc'|'desc', - * } $sortBy + * } $sortBys * @param array{ * example_admin?: bool, * example_website?: bool, * with-example-content?: bool|array, * } $selects */ - private function createQueryBuilder(array $filters, array $sortBy = [], array $selects = []): QueryBuilder + private function createQueryBuilder(array $filters, array $sortBys = [], array $selects = []): QueryBuilder { foreach ($selects as $selectGroup => $value) { if (!$value) { @@ -307,13 +307,18 @@ private function createQueryBuilder(array $filters, array $sortBy = [], array $s $queryBuilder->setFirstResult($offset); } - if (\array_key_exists('locale', $filters) // should also work with locale = null - && \array_key_exists('stage', $filters)) { + if (( + \array_key_exists('locale', $filters) // should also work with locale = null + && \array_key_exists('stage', $filters) + ) + || ([] === $filters && [] !== $sortBys) // if no filters are set, but sortBy is set + ) { $this->dimensionContentQueryEnhancer->addFilters( $queryBuilder, 'example', ExampleDimensionContent::class, - $filters + $filters, + $sortBys ); } diff --git a/Tests/Functional/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancerTest.php b/Tests/Functional/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancerTest.php index 73046833..c2059304 100644 --- a/Tests/Functional/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancerTest.php +++ b/Tests/Functional/Content/Infrastructure/Doctrine/DimensionContentQueryEnhancerTest.php @@ -13,6 +13,7 @@ namespace Sulu\Bundle\ContentBundle\Tests\Functional\Content\Infrastructure\Doctrine; +use Sulu\Bundle\ContentBundle\Content\Application\ContentManager\ContentManagerInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollection; use Sulu\Bundle\ContentBundle\Content\Infrastructure\Doctrine\DimensionContentQueryEnhancer; use Sulu\Bundle\ContentBundle\Tests\Application\ExampleTestBundle\Entity\ExampleDimensionContent; @@ -35,9 +36,12 @@ class DimensionContentQueryEnhancerTest extends SuluTestCase */ private $exampleRepository; + private ContentManagerInterface $contentManager; + protected function setUp(): void { $this->exampleRepository = static::getContainer()->get('example_test.example_repository'); + $this->contentManager = static::getContainer()->get('sulu_content.content_manager'); } public function testNullDimensionAttribute(): void @@ -395,4 +399,195 @@ public function testFilterTemplateKeys(): void 'templateKeys' => ['a', 'c'], ])); } + + public function testSortByInvalidField(): void + { + static::purgeDatabase(); + + $example = static::createExample(); + $example2 = static::createExample(); + $example3 = static::createExample(); + static::createExampleContent($example, ['title' => 'Example A', 'templateKey' => 'a']); + static::createExampleContent($example2, ['title' => 'Example B', 'templateKey' => 'b']); + static::createExampleContent($example3, ['title' => 'Example C', 'templateKey' => 'c']); + static::getEntityManager()->flush(); + static::getEntityManager()->clear(); + + $unsortedResult = \iterator_to_array($this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ] + )); + + foreach ($this->exampleRepository->findBy(['locale' => 'en', 'stage' => 'draft'], ['invalid' => 'asc']) as $key => $example) { + self::assertSame($unsortedResult[$key]->getId(), $example->getId()); + } + + foreach ($this->exampleRepository->findBy(['locale' => 'en', 'stage' => 'draft'], ['invalid' => 'desc']) as $key => $example) { + self::assertSame($unsortedResult[$key]->getId(), $example->getId()); + } + } + + public function testSortByTitle(): void + { + static::purgeDatabase(); + + $example = static::createExample(); + $example2 = static::createExample(); + $example3 = static::createExample(); + static::createExampleContent($example, ['templateData' => ['title' => 'Example A'], 'templateKey' => 'a']); + static::createExampleContent($example2, ['templateData' => ['title' => 'Example B'], 'templateKey' => 'b']); + static::createExampleContent($example3, ['templateData' => ['title' => 'Example C'], 'templateKey' => 'c']); + static::getEntityManager()->flush(); + static::getEntityManager()->clear(); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'title' => 'asc', + ] + ) + ); + $this->assertCount(3, $result); + $this->assertSame('Example A', $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + $this->assertSame('Example B', $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + $this->assertSame('Example C', $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'title' => 'desc', + ] + ) + ); + $this->assertCount(3, $result); + $this->assertSame('Example C', $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + $this->assertSame('Example B', $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + $this->assertSame('Example A', $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft'])->getTemplateData()['title']); + } + + public function testSortByAuthored(): void + { + static::purgeDatabase(); + + $example = static::createExample(); + $example2 = static::createExample(); + $example3 = static::createExample(); + static::createExampleContent($example, ['templateData' => ['title' => 'Example A'], 'authored' => new \DateTimeImmutable('2020-01-01')]); + static::createExampleContent($example2, ['templateData' => ['title' => 'Example B'], 'authored' => new \DateTimeImmutable('2020-03-01')]); + static::createExampleContent($example3, ['templateData' => ['title' => 'Example C'], 'authored' => new \DateTimeImmutable('2020-02-01')]); + static::getEntityManager()->flush(); + static::getEntityManager()->clear(); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'authored' => 'desc', + ] + ) + ); + $this->assertCount(3, $result); + /** @var ExampleDimensionContent $exampleDimensionContent */ + $exampleDimensionContent = $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-03-01', $exampleDimensionContent->getAuthored()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent2 */ + $exampleDimensionContent2 = $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-02-01', $exampleDimensionContent2->getAuthored()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent3 */ + $exampleDimensionContent3 = $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-01-01', $exampleDimensionContent3->getAuthored()?->format('Y-m-d')); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'authored' => 'asc', + ] + ) + ); + $this->assertCount(3, $result); + /** @var ExampleDimensionContent $exampleDimensionContent */ + $exampleDimensionContent = $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-01-01', $exampleDimensionContent->getAuthored()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent2 */ + $exampleDimensionContent2 = $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-02-01', $exampleDimensionContent2->getAuthored()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent3 */ + $exampleDimensionContent3 = $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-03-01', $exampleDimensionContent3->getAuthored()?->format('Y-m-d')); + } + + public function testSortByWorkflowPublished(): void + { + static::purgeDatabase(); + + $example = static::createExample(); + $example2 = static::createExample(); + $example3 = static::createExample(); + static::createExampleContent($example, ['templateData' => ['title' => 'Example A'], 'workflowPublished' => new \DateTimeImmutable('2020-01-01')]); + static::createExampleContent($example2, ['templateData' => ['title' => 'Example B'], 'workflowPublished' => new \DateTimeImmutable('2020-03-01')]); + static::createExampleContent($example3, ['templateData' => ['title' => 'Example C'], 'workflowPublished' => new \DateTimeImmutable('2020-02-01')]); + static::getEntityManager()->flush(); + static::getEntityManager()->clear(); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'workflowPublished' => 'desc', + ] + ) + ); + $this->assertCount(3, $result); + /** @var ExampleDimensionContent $exampleDimensionContent */ + $exampleDimensionContent = $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-03-01', $exampleDimensionContent->getWorkflowPublished()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent2 */ + $exampleDimensionContent2 = $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-02-01', $exampleDimensionContent2->getWorkflowPublished()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent3 */ + $exampleDimensionContent3 = $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-01-01', $exampleDimensionContent3->getWorkflowPublished()?->format('Y-m-d')); + + $result = \iterator_to_array( + $this->exampleRepository->findBy( + [ + 'locale' => 'en', + 'stage' => 'draft', + ], + [ + 'workflowPublished' => 'asc', + ] + ) + ); + $this->assertCount(3, $result); + /** @var ExampleDimensionContent $exampleDimensionContent */ + $exampleDimensionContent = $this->contentManager->resolve($result[0], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-01-01', $exampleDimensionContent->getWorkflowPublished()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent2 */ + $exampleDimensionContent2 = $this->contentManager->resolve($result[1], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-02-01', $exampleDimensionContent2->getWorkflowPublished()?->format('Y-m-d')); + /** @var ExampleDimensionContent $exampleDimensionContent3 */ + $exampleDimensionContent3 = $this->contentManager->resolve($result[2], ['locale' => 'en', 'stage' => 'draft']); + $this->assertSame('2020-03-01', $exampleDimensionContent3->getWorkflowPublished()?->format('Y-m-d')); + } } diff --git a/Tests/Functional/Traits/CreateExampleTrait.php b/Tests/Functional/Traits/CreateExampleTrait.php index 102ba8f7..93d9bacf 100644 --- a/Tests/Functional/Traits/CreateExampleTrait.php +++ b/Tests/Functional/Traits/CreateExampleTrait.php @@ -15,6 +15,7 @@ use Doctrine\ORM\EntityManagerInterface; use Sulu\Bundle\CategoryBundle\Entity\CategoryInterface; +use Sulu\Bundle\ContactBundle\Entity\ContactInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface; use Sulu\Bundle\ContentBundle\Tests\Application\ExampleTestBundle\Entity\Example; use Sulu\Bundle\ContentBundle\Tests\Application\ExampleTestBundle\Entity\ExampleDimensionContent; @@ -40,6 +41,10 @@ public function createExample(): Example * templateData?: mixed[], * excerptCategories?: CategoryInterface[], * excerptTags?: TagInterface[], + * author?: ?ContactInterface, + * authored?: ?\DateTimeImmutable, + * workflowPlace?: ?string, + * workflowPublished?: ?\DateTimeImmutable, * } $data */ public function createExampleContent(Example $example, array $data = []): void @@ -56,6 +61,10 @@ public function createExampleContent(Example $example, array $data = []): void $localizedDimensionContent = $example->createDimensionContent(); $localizedDimensionContent->setLocale($locale); $localizedDimensionContent->setStage($stage); + $localizedDimensionContent->setAuthor($data['author'] ?? null); + $localizedDimensionContent->setAuthored($data['authored'] ?? null); + $localizedDimensionContent->setWorkflowPlace($data['workflowPlace'] ?? null); + $localizedDimensionContent->setWorkflowPublished($data['workflowPublished'] ?? null); $templateKey = $data['templateKey'] ?? null; if ($templateKey) {