Extracted tags filtering params to a DTO

This commit is contained in:
Alejandro Celaya 2022-01-06 09:50:43 +01:00
parent 4b90cf93d3
commit e998c8434d
6 changed files with 55 additions and 20 deletions

View file

@ -8,6 +8,7 @@ use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Happyr\DoctrineSpecification\Spec; use Happyr\DoctrineSpecification\Spec;
use Shlinkio\Shlink\Core\Entity\Tag; use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo; use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
use Shlinkio\Shlink\Core\Tag\Spec\CountTagsWithName; use Shlinkio\Shlink\Core\Tag\Spec\CountTagsWithName;
use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin; use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
@ -32,29 +33,24 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
/** /**
* @return TagInfo[] * @return TagInfo[]
*/ */
public function findTagsWithInfo( public function findTagsWithInfo(?TagsListFiltering $filtering = null): array
?int $limit = null, {
?int $offset = null,
?string $searchTerm = null,
?ApiKey $apiKey = null,
): array {
$qb = $this->createQueryBuilder('t'); $qb = $this->createQueryBuilder('t');
$qb->select('t AS tag', 'COUNT(DISTINCT s.id) AS shortUrlsCount', 'COUNT(DISTINCT v.id) AS visitsCount') $qb->select('t AS tag', 'COUNT(DISTINCT s.id) AS shortUrlsCount', 'COUNT(DISTINCT v.id) AS visitsCount')
->leftJoin('t.shortUrls', 's') ->leftJoin('t.shortUrls', 's')
->leftJoin('s.visits', 'v') ->leftJoin('s.visits', 'v')
->groupBy('t') ->groupBy('t')
->orderBy('t.name', 'ASC') ->orderBy('t.name', 'ASC')
->setMaxResults($limit) ->setMaxResults($filtering?->limit())
->setFirstResult($offset); ->setFirstResult($filtering?->offset());
$apiKey = $filtering?->apiKey();
if ($apiKey !== null) { if ($apiKey !== null) {
$this->applySpecification($qb, $apiKey->spec(false, 'shortUrls'), 't'); $this->applySpecification($qb, $apiKey->spec(false, 'shortUrls'), 't');
} }
$query = $qb->getQuery();
return map( return map(
$query->getResult(), $qb->getQuery()->getResult(),
static fn (array $row) => new TagInfo($row['tag'], (int) $row['shortUrlsCount'], (int) $row['visitsCount']), static fn (array $row) => new TagInfo($row['tag'], (int) $row['shortUrlsCount'], (int) $row['visitsCount']),
); );
} }

View file

@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\Repository;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo; use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
@ -16,12 +17,7 @@ interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRe
/** /**
* @return TagInfo[] * @return TagInfo[]
*/ */
public function findTagsWithInfo( public function findTagsWithInfo(?TagsListFiltering $filtering = null): array;
?int $limit = null,
?int $offset = null,
?string $searchTerm = null,
?ApiKey $apiKey = null,
): array;
public function tagExists(string $tag, ?ApiKey $apiKey = null): bool; public function tagExists(string $tag, ?ApiKey $apiKey = null): bool;
} }

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag\Model;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
final class TagsListFiltering
{
public function __construct(
private ?int $limit = null,
private ?int $offset = null,
private ?string $searchTerm = null,
private ?ApiKey $apiKey = null,
) {
}
public function limit(): ?int
{
return $this->limit;
}
public function offset(): ?int
{
return $this->offset;
}
public function searchTerm(): ?string
{
return $this->searchTerm;
}
public function apiKey(): ?ApiKey
{
return $this->apiKey;
}
}

View file

@ -4,10 +4,14 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
class TagsInfoPaginatorAdapter extends AbstractTagsPaginatorAdapter class TagsInfoPaginatorAdapter extends AbstractTagsPaginatorAdapter
{ {
public function getSlice(int $offset, int $length): iterable public function getSlice(int $offset, int $length): iterable
{ {
return $this->repo->findTagsWithInfo($length, $offset, null, $this->apiKey); return $this->repo->findTagsWithInfo(
new TagsListFiltering($length, $offset, $this->params->searchTerm(), $this->apiKey),
);
} }
} }

View file

@ -74,7 +74,7 @@ class TagRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance()));
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
$result = $this->repo->findTagsWithInfo(); $result = $this->repo->findTagsWithInfo(); // TODO Test with some filters
self::assertCount(4, $result); self::assertCount(4, $result);
self::assertEquals(0, $result[0]->shortUrlsCount()); self::assertEquals(0, $result[0]->shortUrlsCount());

View file

@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
use Shlinkio\Shlink\Core\Repository\TagRepository; use Shlinkio\Shlink\Core\Repository\TagRepository;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo; use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagRenaming; use Shlinkio\Shlink\Core\Tag\Model\TagRenaming;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
use Shlinkio\Shlink\Core\Tag\Model\TagsParams; use Shlinkio\Shlink\Core\Tag\Model\TagsParams;
use Shlinkio\Shlink\Core\Tag\TagService; use Shlinkio\Shlink\Core\Tag\TagService;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta; use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
@ -64,7 +65,7 @@ class TagServiceTest extends TestCase
{ {
$expected = [new TagInfo(new Tag('foo'), 1, 1), new TagInfo(new Tag('bar'), 3, 10)]; $expected = [new TagInfo(new Tag('foo'), 1, 1), new TagInfo(new Tag('bar'), 3, 10)];
$find = $this->repo->findTagsWithInfo(2, 0, null, $apiKey)->willReturn($expected); $find = $this->repo->findTagsWithInfo(new TagsListFiltering(2, 0, null, $apiKey))->willReturn($expected);
$count = $this->repo->matchSingleScalarResult(Argument::cetera())->willReturn(2); $count = $this->repo->matchSingleScalarResult(Argument::cetera())->willReturn(2);
$result = $this->service->tagsInfo(TagsParams::fromRawData([]), $apiKey); // TODO Add more cases with params $result = $this->service->tagsInfo(TagsParams::fromRawData([]), $apiKey); // TODO Add more cases with params