diff --git a/docs/swagger/paths/v1_tags.json b/docs/swagger/paths/v1_tags.json index 187f184d..1e36e112 100644 --- a/docs/swagger/paths/v1_tags.json +++ b/docs/swagger/paths/v1_tags.json @@ -54,6 +54,19 @@ "schema": { "type": "string" } + }, + { + "name": "orderBy", + "in": "query", + "description": "To determine how to order the results.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "tag-ASC", + "tag-DESC" + ] + } } ], "responses": { diff --git a/module/Core/src/Repository/TagRepository.php b/module/Core/src/Repository/TagRepository.php index 5793de67..0182908c 100644 --- a/module/Core/src/Repository/TagRepository.php +++ b/module/Core/src/Repository/TagRepository.php @@ -50,7 +50,7 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito $conn = $this->getEntityManager()->getConnection(); $subQb = $this->createQueryBuilder('t'); $subQb->select('t.id', 't.name') - ->orderBy('t.name', 'ASC') // TODO Make dynamic + ->orderBy('t.name', $filtering?->orderBy()?->orderDirection() ?? 'ASC') // TODO Make filed dynamic ->setMaxResults($filtering?->limit() ?? PHP_INT_MAX) ->setFirstResult($filtering?->offset() ?? 0); @@ -96,7 +96,7 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito ->leftJoin('st', 'short_urls', 's', $nativeQb->expr()->eq('s.id', 'st.short_url_id')) ->leftJoin('st', 'visits', 'v', $nativeQb->expr()->eq('s.id', 'v.short_url_id')) ->groupBy('t.id_0', 't.name_1') - ->orderBy('t.name_1', 'ASC'); // TODO Make dynamic + ->orderBy('t.name_1', $filtering?->orderBy()?->orderDirection() ?? 'ASC'); // TODO Make field dynamic // Apply API key role conditions to the native query too, as they will affect the amounts on the aggregates $apiKey?->mapRoles(fn (string $roleName, array $meta) => match ($roleName) { diff --git a/module/Core/test-db/Repository/TagRepositoryTest.php b/module/Core/test-db/Repository/TagRepositoryTest.php index ab4e07ad..5ac1f3ac 100644 --- a/module/Core/test-db/Repository/TagRepositoryTest.php +++ b/module/Core/test-db/Repository/TagRepositoryTest.php @@ -8,6 +8,7 @@ use Shlinkio\Shlink\Core\Entity\Domain; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\Tag; use Shlinkio\Shlink\Core\Entity\Visit; +use Shlinkio\Shlink\Core\Model\Ordering; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Repository\TagRepository; @@ -86,7 +87,7 @@ class TagRepositoryTest extends DatabaseTestCase public function provideFilters(): iterable { - $noFiltersAsserts = static function (array $result, array $tagNames): void { + $defaultAsserts = static function (array $result, array $tagNames): void { /** @var TagInfo[] $result */ self::assertCount(4, $result); self::assertEquals(0, $result[0]->shortUrlsCount()); @@ -106,8 +107,8 @@ class TagRepositoryTest extends DatabaseTestCase self::assertEquals($tagNames[0], $result[3]->tag()->__toString()); }; - yield 'no filter' => [null, $noFiltersAsserts]; - yield 'empty filter' => [new TagsListFiltering(), $noFiltersAsserts]; + yield 'no filter' => [null, $defaultAsserts]; + yield 'empty filter' => [new TagsListFiltering(), $defaultAsserts]; yield 'limit' => [new TagsListFiltering(2), static function (array $result, array $tagNames): void { /** @var TagInfo[] $result */ self::assertCount(2, $result); @@ -154,6 +155,32 @@ class TagRepositoryTest extends DatabaseTestCase self::assertEquals($tagNames[2], $result[1]->tag()->__toString()); }, ]; + yield 'ASC ordering' => [ + new TagsListFiltering(null, null, null, Ordering::fromTuple(['tag', 'ASC'])), + $defaultAsserts, + ]; + yield 'DESC ordering' => [ + new TagsListFiltering(null, null, null, Ordering::fromTuple(['tag', 'DESC'])), + static function (array $result, array $tagNames): void { + /** @var TagInfo[] $result */ + self::assertCount(4, $result); + self::assertEquals(0, $result[3]->shortUrlsCount()); + self::assertEquals(0, $result[3]->visitsCount()); + self::assertEquals($tagNames[3], $result[3]->tag()->__toString()); + + self::assertEquals(1, $result[2]->shortUrlsCount()); + self::assertEquals(3, $result[2]->visitsCount()); + self::assertEquals($tagNames[1], $result[2]->tag()->__toString()); + + self::assertEquals(1, $result[1]->shortUrlsCount()); + self::assertEquals(3, $result[1]->visitsCount()); + self::assertEquals($tagNames[2], $result[1]->tag()->__toString()); + + self::assertEquals(2, $result[0]->shortUrlsCount()); + self::assertEquals(4, $result[0]->visitsCount()); + self::assertEquals($tagNames[0], $result[0]->tag()->__toString()); + }, + ]; } /** @test */ diff --git a/module/Core/test-db/Tag/Paginator/Adapter/TagsPaginatorAdapterTest.php b/module/Core/test-db/Tag/Paginator/Adapter/TagsPaginatorAdapterTest.php index f8cd2e8e..d906f80c 100644 --- a/module/Core/test-db/Tag/Paginator/Adapter/TagsPaginatorAdapterTest.php +++ b/module/Core/test-db/Tag/Paginator/Adapter/TagsPaginatorAdapterTest.php @@ -61,9 +61,9 @@ class TagsPaginatorAdapterTest extends DatabaseTestCase yield ['ba', null, 0, 1, ['bar'], 2]; yield ['foo', null, 0, 10, ['foo'], 1]; yield ['a', null, 0, 10, ['another', 'bar', 'baz'], 3]; - yield [null, 'name-DESC', 0, 10, ['foo', 'baz', 'bar', 'another'], 4]; - yield [null, 'name-ASC', 0, 10, ['another', 'bar', 'baz', 'foo'], 4]; - yield [null, 'name-DESC', 0, 2, ['foo', 'baz'], 4]; - yield ['ba', 'name-DESC', 0, 1, ['baz'], 2]; + yield [null, 'tag-DESC', 0, 10, ['foo', 'baz', 'bar', 'another'], 4]; + yield [null, 'tag-ASC', 0, 10, ['another', 'bar', 'baz', 'foo'], 4]; + yield [null, 'tag-DESC', 0, 2, ['foo', 'baz'], 4]; + yield ['ba', 'tag-DESC', 0, 1, ['baz'], 2]; } }