mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-22 08:56:42 -06:00
Fixed ordering in tags supporting more fields
This commit is contained in:
parent
961178fd82
commit
ce9ec0d738
@ -9,22 +9,26 @@ use function Shlinkio\Shlink\Core\camelCaseToSnakeCase;
|
|||||||
enum OrderableField: string
|
enum OrderableField: string
|
||||||
{
|
{
|
||||||
case TAG = 'tag';
|
case TAG = 'tag';
|
||||||
// case SHORT_URLS = 'shortUrls';
|
case SHORT_URLS_COUNT = 'shortUrlsCount';
|
||||||
// case VISITS = 'visits';
|
case VISITS = 'visits';
|
||||||
// case NON_BOT_VISITS = 'nonBotVisits';
|
case NON_BOT_VISITS = 'nonBotVisits';
|
||||||
|
|
||||||
/** @deprecated Use VISITS instead */
|
/** @deprecated Use VISITS instead */
|
||||||
case VISITS_COUNT = 'visitsCount';
|
case VISITS_COUNT = 'visitsCount';
|
||||||
/** @deprecated Use SHORT_URLS instead */
|
|
||||||
case SHORT_URLS_COUNT = 'shortUrlsCount';
|
|
||||||
|
|
||||||
public static function isAggregateField(string $field): bool
|
public static function isAggregateField(string $field): bool
|
||||||
{
|
{
|
||||||
return $field === self::SHORT_URLS_COUNT->value || $field === self::VISITS_COUNT->value;
|
$parsed = self::tryFrom($field);
|
||||||
|
return $parsed !== null && $parsed !== self::TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function toSnakeCaseValidField(?string $field): string
|
public static function toSnakeCaseValidField(?string $field): string
|
||||||
{
|
{
|
||||||
return camelCaseToSnakeCase($field === self::SHORT_URLS_COUNT->value ? $field : self::VISITS_COUNT->value);
|
$parsed = self::tryFrom($field);
|
||||||
|
$normalized = match ($parsed) {
|
||||||
|
self::VISITS_COUNT, null => self::VISITS,
|
||||||
|
default => $parsed,
|
||||||
|
};
|
||||||
|
|
||||||
|
return camelCaseToSnakeCase($normalized->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ final class TagInfo implements JsonSerializable
|
|||||||
return new self(
|
return new self(
|
||||||
$data['tag'],
|
$data['tag'],
|
||||||
(int) $data['shortUrlsCount'],
|
(int) $data['shortUrlsCount'],
|
||||||
(int) $data['visitsCount'],
|
(int) $data['visits'],
|
||||||
isset($data['nonBotVisitsCount']) ? (int) $data['nonBotVisitsCount'] : null,
|
isset($data['nonBotVisits']) ? (int) $data['nonBotVisits'] : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
|
|||||||
't.id_0 AS id',
|
't.id_0 AS id',
|
||||||
't.name_1 AS name',
|
't.name_1 AS name',
|
||||||
'COUNT(DISTINCT s.id) AS short_urls_count',
|
'COUNT(DISTINCT s.id) AS short_urls_count',
|
||||||
'COUNT(DISTINCT v.id) AS visits_count', // Native queries require snake_case for cross-db compatibility
|
'COUNT(DISTINCT v.id) AS visits', // Native queries require snake_case for cross-db compatibility
|
||||||
'COUNT(DISTINCT v2.id) AS non_bot_visits_count',
|
'COUNT(DISTINCT v2.id) AS non_bot_visits',
|
||||||
)
|
)
|
||||||
->from('(' . $subQb->getQuery()->getSQL() . ')', 't') // @phpstan-ignore-line
|
->from('(' . $subQb->getQuery()->getSQL() . ')', 't') // @phpstan-ignore-line
|
||||||
->leftJoin('t', 'short_urls_in_tags', 'st', $nativeQb->expr()->eq('t.id_0', 'st.tag_id'))
|
->leftJoin('t', 'short_urls_in_tags', 'st', $nativeQb->expr()->eq('t.id_0', 'st.tag_id'))
|
||||||
@ -108,8 +108,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
|
|||||||
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
|
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
|
||||||
$rsm->addScalarResult('name', 'tag');
|
$rsm->addScalarResult('name', 'tag');
|
||||||
$rsm->addScalarResult('short_urls_count', 'shortUrlsCount');
|
$rsm->addScalarResult('short_urls_count', 'shortUrlsCount');
|
||||||
$rsm->addScalarResult('visits_count', 'visitsCount');
|
$rsm->addScalarResult('visits', 'visits');
|
||||||
$rsm->addScalarResult('non_bot_visits_count', 'nonBotVisitsCount');
|
$rsm->addScalarResult('non_bot_visits', 'nonBotVisits');
|
||||||
|
|
||||||
return map(
|
return map(
|
||||||
$this->getEntityManager()->createNativeQuery($nativeQb->getSQL(), $rsm)->getResult(),
|
$this->getEntityManager()->createNativeQuery($nativeQb->getSQL(), $rsm)->getResult(),
|
||||||
|
@ -10,6 +10,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
|||||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
|
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
|
||||||
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
|
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
|
||||||
|
use Shlinkio\Shlink\Core\Tag\Model\OrderableField;
|
||||||
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
|
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
|
||||||
use Shlinkio\Shlink\Core\Tag\Repository\TagRepository;
|
use Shlinkio\Shlink\Core\Tag\Repository\TagRepository;
|
||||||
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
||||||
@ -135,17 +136,21 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
['baz', 1, 3, 2],
|
['baz', 1, 3, 2],
|
||||||
]];
|
]];
|
||||||
yield 'ASC ordering' => [
|
yield 'ASC ordering' => [
|
||||||
new TagsListFiltering(null, null, null, Ordering::fromTuple(['tag', 'ASC'])),
|
new TagsListFiltering(null, null, null, Ordering::fromTuple([OrderableField::TAG->value, 'ASC'])),
|
||||||
$defaultList,
|
$defaultList,
|
||||||
];
|
];
|
||||||
yield 'DESC ordering' => [new TagsListFiltering(null, null, null, Ordering::fromTuple(['tag', 'DESC'])), [
|
yield 'DESC ordering' => [new TagsListFiltering(null, null, null, Ordering::fromTuple(
|
||||||
|
[OrderableField::TAG->value, 'DESC'],
|
||||||
|
)), [
|
||||||
['foo', 2, 4, 3],
|
['foo', 2, 4, 3],
|
||||||
['baz', 1, 3, 2],
|
['baz', 1, 3, 2],
|
||||||
['bar', 3, 3, 2],
|
['bar', 3, 3, 2],
|
||||||
['another', 0, 0, 0],
|
['another', 0, 0, 0],
|
||||||
]];
|
]];
|
||||||
yield 'short URLs count ASC ordering' => [
|
yield 'short URLs count ASC ordering' => [
|
||||||
new TagsListFiltering(null, null, null, Ordering::fromTuple(['shortUrlsCount', 'ASC'])),
|
new TagsListFiltering(null, null, null, Ordering::fromTuple(
|
||||||
|
[OrderableField::SHORT_URLS_COUNT->value, 'ASC'],
|
||||||
|
)),
|
||||||
[
|
[
|
||||||
['another', 0, 0, 0],
|
['another', 0, 0, 0],
|
||||||
['baz', 1, 3, 2],
|
['baz', 1, 3, 2],
|
||||||
@ -154,7 +159,9 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
yield 'short URLs count DESC ordering' => [
|
yield 'short URLs count DESC ordering' => [
|
||||||
new TagsListFiltering(null, null, null, Ordering::fromTuple(['shortUrlsCount', 'DESC'])),
|
new TagsListFiltering(null, null, null, Ordering::fromTuple(
|
||||||
|
[OrderableField::SHORT_URLS_COUNT->value, 'DESC'],
|
||||||
|
)),
|
||||||
[
|
[
|
||||||
['bar', 3, 3, 2],
|
['bar', 3, 3, 2],
|
||||||
['foo', 2, 4, 3],
|
['foo', 2, 4, 3],
|
||||||
@ -163,7 +170,18 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
yield 'visits count ASC ordering' => [
|
yield 'visits count ASC ordering' => [
|
||||||
new TagsListFiltering(null, null, null, Ordering::fromTuple(['visitsCount', 'ASC'])),
|
new TagsListFiltering(null, null, null, Ordering::fromTuple([OrderableField::VISITS->value, 'ASC'])),
|
||||||
|
[
|
||||||
|
['another', 0, 0, 0],
|
||||||
|
['bar', 3, 3, 2],
|
||||||
|
['baz', 1, 3, 2],
|
||||||
|
['foo', 2, 4, 3],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
yield 'non-bot visits count ASC ordering' => [
|
||||||
|
new TagsListFiltering(null, null, null, Ordering::fromTuple(
|
||||||
|
[OrderableField::NON_BOT_VISITS->value, 'ASC'],
|
||||||
|
)),
|
||||||
[
|
[
|
||||||
['another', 0, 0, 0],
|
['another', 0, 0, 0],
|
||||||
['bar', 3, 3, 2],
|
['bar', 3, 3, 2],
|
||||||
@ -172,7 +190,7 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
yield 'visits count DESC ordering' => [
|
yield 'visits count DESC ordering' => [
|
||||||
new TagsListFiltering(null, null, null, Ordering::fromTuple(['visitsCount', 'DESC'])),
|
new TagsListFiltering(null, null, null, Ordering::fromTuple([OrderableField::VISITS->value, 'DESC'])),
|
||||||
[
|
[
|
||||||
['foo', 2, 4, 3],
|
['foo', 2, 4, 3],
|
||||||
['bar', 3, 3, 2],
|
['bar', 3, 3, 2],
|
||||||
@ -181,7 +199,7 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
yield 'visits count DESC ordering and limit' => [
|
yield 'visits count DESC ordering and limit' => [
|
||||||
new TagsListFiltering(2, null, null, Ordering::fromTuple(['visitsCount', 'DESC'])),
|
new TagsListFiltering(2, null, null, Ordering::fromTuple([OrderableField::VISITS_COUNT->value, 'DESC'])),
|
||||||
[
|
[
|
||||||
['foo', 2, 4, 3],
|
['foo', 2, 4, 3],
|
||||||
['bar', 3, 3, 2],
|
['bar', 3, 3, 2],
|
||||||
@ -195,11 +213,11 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
['foo', 1, 3, 2],
|
['foo', 1, 3, 2],
|
||||||
]];
|
]];
|
||||||
yield 'combined' => [new TagsListFiltering(1, null, null, Ordering::fromTuple(
|
yield 'combined' => [new TagsListFiltering(1, null, null, Ordering::fromTuple(
|
||||||
['shortUrls', 'DESC'],
|
[OrderableField::SHORT_URLS_COUNT->value, 'DESC'],
|
||||||
), ApiKey::fromMeta(
|
), ApiKey::fromMeta(
|
||||||
ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()),
|
ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()),
|
||||||
)), [
|
)), [
|
||||||
['foo', 1, 3, 2],
|
['bar', 2, 3, 2],
|
||||||
]];
|
]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user