mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-22 08:56:42 -06:00
Added support to filter out expired short URLs from list
This commit is contained in:
parent
805c8c87ba
commit
463dfe9729
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ShortUrl\Repository;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
@ -11,7 +12,6 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Doctrine\Type\ChronosDateTimeType;
|
||||
use Shlinkio\Shlink\Core\Model\Ordering;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
@ -38,43 +38,58 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
|
||||
// In case the ordering has been specified, the query could be more complex. Process it
|
||||
if ($filtering->orderBy->hasOrderField()) {
|
||||
return $this->processOrderByForList($qb, $filtering->orderBy);
|
||||
$this->processOrderByForList($qb, $filtering);
|
||||
}
|
||||
|
||||
// With no explicit order by, fallback to dateCreated-DESC
|
||||
return $qb->orderBy('s.dateCreated', 'DESC')->getQuery()->getResult();
|
||||
$result = $qb->getQuery()->getResult();
|
||||
if ($filtering->excludeMaxVisitsReached || $filtering->orderBy->field === 'visits') {
|
||||
return array_column($result, 0);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function processOrderByForList(QueryBuilder $qb, Ordering $orderBy): array
|
||||
private function processOrderByForList(QueryBuilder $qb, ShortUrlsListFiltering $filtering): void
|
||||
{
|
||||
$fieldName = $orderBy->field;
|
||||
$order = $orderBy->direction;
|
||||
$fieldName = $filtering->orderBy->field;
|
||||
$order = $filtering->orderBy->direction;
|
||||
|
||||
if ($fieldName === 'visits') {
|
||||
// FIXME This query is inefficient.
|
||||
// Diagnostic: It might need to use a sub-query, as done with the tags list query.
|
||||
$qb->addSelect('COUNT(DISTINCT v) AS totalVisits')
|
||||
->leftJoin('s.visits', 'v')
|
||||
->groupBy('s')
|
||||
->orderBy('totalVisits', $order);
|
||||
if (! $filtering->excludeMaxVisitsReached) {
|
||||
// Left join only if this was not true, otherwise this left join already happened
|
||||
$this->leftJoinShortUrlsWithVisitsCount($qb);
|
||||
}
|
||||
|
||||
return array_column($qb->getQuery()->getResult(), 0);
|
||||
}
|
||||
|
||||
$orderableFields = ['longUrl', 'shortCode', 'dateCreated', 'title'];
|
||||
if (contains($orderableFields, $fieldName)) {
|
||||
$qb->orderBy('totalVisits', $order);
|
||||
} elseif (contains(['longUrl', 'shortCode', 'dateCreated', 'title'], $fieldName)) {
|
||||
$qb->orderBy('s.' . $fieldName, $order);
|
||||
} else {
|
||||
// With no explicit order by, fallback to dateCreated-DESC
|
||||
$qb->orderBy('s.dateCreated', 'DESC');
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function countList(ShortUrlsCountFiltering $filtering): int
|
||||
{
|
||||
$qb = $this->createListQueryBuilder($filtering);
|
||||
$qb->select('COUNT(DISTINCT s)');
|
||||
$query = $qb->getQuery();
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||
// dump($query->getSQL());
|
||||
|
||||
// TODO This is crap...
|
||||
return $filtering->excludeMaxVisitsReached
|
||||
? count($query->getSingleColumnResult())
|
||||
: (int) $query->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function leftJoinShortUrlsWithVisitsCount(QueryBuilder $qb): void
|
||||
{
|
||||
$qb->addSelect('COUNT(DISTINCT v) AS totalVisits')
|
||||
->leftJoin('s.visits', 'v')
|
||||
->groupBy('s');
|
||||
}
|
||||
|
||||
private function createListQueryBuilder(ShortUrlsCountFiltering $filtering): QueryBuilder
|
||||
@ -134,6 +149,22 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
: $this->joinAllTags($qb, $tags);
|
||||
}
|
||||
|
||||
if ($filtering->excludeMaxVisitsReached) {
|
||||
$this->leftJoinShortUrlsWithVisitsCount($qb);
|
||||
$qb->having($qb->expr()->orX(
|
||||
$qb->expr()->isNull('s.maxVisits'),
|
||||
$qb->expr()->gt('s.maxVisits', 'COUNT(DISTINCT v)'),
|
||||
));
|
||||
}
|
||||
|
||||
if ($filtering->excludePastValidUntil) {
|
||||
$qb->andWhere($qb->expr()->orX(
|
||||
$qb->expr()->isNull('s.validUntil'),
|
||||
$qb->expr()->gte('s.validUntil', ':minValidSince'),
|
||||
))
|
||||
->setParameter('minValidSince', Chronos::now()->toDateTimeString());
|
||||
}
|
||||
|
||||
$this->applySpecification($qb, $filtering->apiKey?->spec(), 's');
|
||||
|
||||
return $qb;
|
||||
|
Loading…
Reference in New Issue
Block a user