mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-16 23:39:54 +03:00
Added support for a new tagsMode param when listing short URLs
This commit is contained in:
parent
d0daeb0078
commit
103af2e2c1
7 changed files with 64 additions and 14 deletions
|
@ -14,11 +14,15 @@ use function Shlinkio\Shlink\Core\parseDateField;
|
|||
final class ShortUrlsParams
|
||||
{
|
||||
public const DEFAULT_ITEMS_PER_PAGE = 10;
|
||||
public const TAGS_MODE_ANY = 'any';
|
||||
public const TAGS_MODE_ALL = 'all';
|
||||
|
||||
private int $page;
|
||||
private int $itemsPerPage;
|
||||
private ?string $searchTerm;
|
||||
private array $tags;
|
||||
/** @var self::TAGS_MODE_ANY|self::TAGS_MODE_ALL */
|
||||
private string $tagsMode = self::TAGS_MODE_ANY;
|
||||
private ShortUrlsOrdering $orderBy;
|
||||
private ?DateRange $dateRange;
|
||||
|
||||
|
@ -63,6 +67,7 @@ final class ShortUrlsParams
|
|||
$this->itemsPerPage = (int) (
|
||||
$inputFilter->getValue(ShortUrlsParamsInputFilter::ITEMS_PER_PAGE) ?? self::DEFAULT_ITEMS_PER_PAGE
|
||||
);
|
||||
$this->tagsMode = $inputFilter->getValue(ShortUrlsParamsInputFilter::TAGS_MODE) ?? self::TAGS_MODE_ANY;
|
||||
}
|
||||
|
||||
public function page(): int
|
||||
|
@ -94,4 +99,12 @@ final class ShortUrlsParams
|
|||
{
|
||||
return $this->dateRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self::TAGS_MODE_ANY|self::TAGS_MODE_ALL
|
||||
*/
|
||||
public function tagsMode(): string
|
||||
{
|
||||
return $this->tagsMode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class ShortUrlRepositoryAdapter implements AdapterInterface
|
|||
$offset,
|
||||
$this->params->searchTerm(),
|
||||
$this->params->tags(),
|
||||
$this->params->tagsMode(),
|
||||
$this->params->orderBy(),
|
||||
$this->params->dateRange(),
|
||||
$this->apiKey?->spec(),
|
||||
|
@ -36,6 +37,7 @@ class ShortUrlRepositoryAdapter implements AdapterInterface
|
|||
return $this->repository->countList(
|
||||
$this->params->searchTerm(),
|
||||
$this->params->tags(),
|
||||
$this->params->tagsMode(),
|
||||
$this->params->dateRange(),
|
||||
$this->apiKey?->spec(),
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
|
@ -15,6 +16,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
|||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsOrdering;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
|
||||
|
||||
use function array_column;
|
||||
|
@ -32,11 +34,12 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
|||
?int $offset = null,
|
||||
?string $searchTerm = null,
|
||||
array $tags = [],
|
||||
string $tagsMode = ShortUrlsParams::TAGS_MODE_ANY,
|
||||
?ShortUrlsOrdering $orderBy = null,
|
||||
?DateRange $dateRange = null,
|
||||
?Specification $spec = null,
|
||||
): array {
|
||||
$qb = $this->createListQueryBuilder($searchTerm, $tags, $dateRange, $spec);
|
||||
$qb = $this->createListQueryBuilder($searchTerm, $tags, $tagsMode, $dateRange, $spec);
|
||||
$qb->select('DISTINCT s')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
@ -77,10 +80,11 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
|||
public function countList(
|
||||
?string $searchTerm = null,
|
||||
array $tags = [],
|
||||
string $tagsMode = ShortUrlsParams::TAGS_MODE_ANY,
|
||||
?DateRange $dateRange = null,
|
||||
?Specification $spec = null,
|
||||
): int {
|
||||
$qb = $this->createListQueryBuilder($searchTerm, $tags, $dateRange, $spec);
|
||||
$qb = $this->createListQueryBuilder($searchTerm, $tags, $tagsMode, $dateRange, $spec);
|
||||
$qb->select('COUNT(DISTINCT s)');
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||
|
@ -89,6 +93,7 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
|||
private function createListQueryBuilder(
|
||||
?string $searchTerm,
|
||||
array $tags,
|
||||
string $tagsMode,
|
||||
?DateRange $dateRange,
|
||||
?Specification $spec,
|
||||
): QueryBuilder {
|
||||
|
@ -139,8 +144,8 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
|||
{
|
||||
// When ordering DESC, Postgres puts nulls at the beginning while the rest of supported DB engines put them at
|
||||
// the bottom
|
||||
$dbPlatform = $this->getEntityManager()->getConnection()->getDatabasePlatform()->getName();
|
||||
$ordering = $dbPlatform === 'postgresql' ? 'ASC' : 'DESC';
|
||||
$dbPlatform = $this->getEntityManager()->getConnection()->getDatabasePlatform();
|
||||
$ordering = $dbPlatform instanceof PostgreSQLPlatform ? 'ASC' : 'DESC';
|
||||
|
||||
$dql = <<<DQL
|
||||
SELECT s
|
||||
|
|
|
@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
|||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsOrdering;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
|
||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
|
||||
|
||||
interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
|
||||
|
@ -21,6 +22,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat
|
|||
?int $offset = null,
|
||||
?string $searchTerm = null,
|
||||
array $tags = [],
|
||||
string $tagsMode = ShortUrlsParams::TAGS_MODE_ANY,
|
||||
?ShortUrlsOrdering $orderBy = null,
|
||||
?DateRange $dateRange = null,
|
||||
?Specification $spec = null,
|
||||
|
@ -29,6 +31,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat
|
|||
public function countList(
|
||||
?string $searchTerm = null,
|
||||
array $tags = [],
|
||||
string $tagsMode = ShortUrlsParams::TAGS_MODE_ANY,
|
||||
?DateRange $dateRange = null,
|
||||
?Specification $spec = null,
|
||||
): int;
|
||||
|
|
|
@ -5,8 +5,10 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Validation;
|
||||
|
||||
use Laminas\InputFilter\InputFilter;
|
||||
use Laminas\Validator\InArray;
|
||||
use Shlinkio\Shlink\Common\Paginator\Paginator;
|
||||
use Shlinkio\Shlink\Common\Validation;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
|
||||
|
||||
class ShortUrlsParamsInputFilter extends InputFilter
|
||||
{
|
||||
|
@ -18,6 +20,7 @@ class ShortUrlsParamsInputFilter extends InputFilter
|
|||
public const START_DATE = 'startDate';
|
||||
public const END_DATE = 'endDate';
|
||||
public const ITEMS_PER_PAGE = 'itemsPerPage';
|
||||
public const TAGS_MODE = 'tagsMode';
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
|
@ -36,5 +39,12 @@ class ShortUrlsParamsInputFilter extends InputFilter
|
|||
$this->add($this->createNumericInput(self::ITEMS_PER_PAGE, false, Paginator::ALL_ITEMS));
|
||||
|
||||
$this->add($this->createTagsInput(self::TAGS, false));
|
||||
|
||||
$tagsMode = $this->createInput(self::TAGS_MODE, false);
|
||||
$tagsMode->getValidatorChain()->attach(new InArray([
|
||||
'haystack' => [ShortUrlsParams::TAGS_MODE_ALL, ShortUrlsParams::TAGS_MODE_ANY],
|
||||
'strict' => InArray::COMPARE_STRICT,
|
||||
]));
|
||||
$this->add($tagsMode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\Entity\Visit;
|
|||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsOrdering;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
|
||||
use Shlinkio\Shlink\Core\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
|
||||
|
@ -127,22 +128,31 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
|||
|
||||
self::assertCount(1, $this->repo->findList(2, 2));
|
||||
|
||||
$result = $this->repo->findList(null, null, null, [], ShortUrlsOrdering::fromRawData([
|
||||
$tagsModeAll = ShortUrlsParams::TAGS_MODE_ANY;
|
||||
$result = $this->repo->findList(null, null, null, [], $tagsModeAll, ShortUrlsOrdering::fromRawData([
|
||||
'orderBy' => 'visits-DESC',
|
||||
]));
|
||||
self::assertCount(3, $result);
|
||||
self::assertSame($bar, $result[0]);
|
||||
|
||||
$result = $this->repo->findList(null, null, null, [], null, DateRange::withEndDate(Chronos::now()->subDays(2)));
|
||||
$result = $this->repo->findList(null, null, null, [], $tagsModeAll, null, DateRange::withEndDate(
|
||||
Chronos::now()->subDays(2),
|
||||
));
|
||||
self::assertCount(1, $result);
|
||||
self::assertEquals(1, $this->repo->countList(null, [], DateRange::withEndDate(Chronos::now()->subDays(2))));
|
||||
self::assertEquals(1, $this->repo->countList(null, [], $tagsModeAll, DateRange::withEndDate(
|
||||
Chronos::now()->subDays(2),
|
||||
)));
|
||||
self::assertSame($foo2, $result[0]);
|
||||
|
||||
self::assertCount(
|
||||
2,
|
||||
$this->repo->findList(null, null, null, [], null, DateRange::withStartDate(Chronos::now()->subDays(2))),
|
||||
$this->repo->findList(null, null, null, [], $tagsModeAll, null, DateRange::withStartDate(
|
||||
Chronos::now()->subDays(2),
|
||||
)),
|
||||
);
|
||||
self::assertEquals(2, $this->repo->countList(null, [], DateRange::withStartDate(Chronos::now()->subDays(2))));
|
||||
self::assertEquals(2, $this->repo->countList(null, [], $tagsModeAll, DateRange::withStartDate(
|
||||
Chronos::now()->subDays(2),
|
||||
)));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -155,9 +165,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
|||
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$result = $this->repo->findList(null, null, null, [], ShortUrlsOrdering::fromRawData([
|
||||
'orderBy' => 'longUrl-ASC',
|
||||
]));
|
||||
$result = $this->repo->findList(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
[],
|
||||
ShortUrlsParams::TAGS_MODE_ANY,
|
||||
ShortUrlsOrdering::fromRawData(['orderBy' => 'longUrl-ASC']),
|
||||
);
|
||||
|
||||
self::assertCount(count($urls), $result);
|
||||
self::assertEquals('a', $result[0]->getLongUrl());
|
||||
|
|
|
@ -46,7 +46,8 @@ class ShortUrlRepositoryAdapterTest extends TestCase
|
|||
$orderBy = $params->orderBy();
|
||||
$dateRange = $params->dateRange();
|
||||
|
||||
$this->repo->findList(10, 5, $searchTerm, $tags, $orderBy, $dateRange, null)->shouldBeCalledOnce();
|
||||
$this->repo->findList(10, 5, $searchTerm, $tags, ShortUrlsParams::TAGS_MODE_ANY, $orderBy, $dateRange, null)
|
||||
->shouldBeCalledOnce();
|
||||
$adapter->getSlice(5, 10);
|
||||
}
|
||||
|
||||
|
@ -70,7 +71,8 @@ class ShortUrlRepositoryAdapterTest extends TestCase
|
|||
$adapter = new ShortUrlRepositoryAdapter($this->repo->reveal(), $params, $apiKey);
|
||||
$dateRange = $params->dateRange();
|
||||
|
||||
$this->repo->countList($searchTerm, $tags, $dateRange, $apiKey->spec())->shouldBeCalledOnce();
|
||||
$this->repo->countList($searchTerm, $tags, ShortUrlsParams::TAGS_MODE_ANY, $dateRange, $apiKey->spec())
|
||||
->shouldBeCalledOnce();
|
||||
$adapter->getNbResults();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue