Ensured domain is taken into account when looking for a short URL

This commit is contained in:
Alejandro Celaya 2019-10-04 17:21:22 +02:00
parent 2ffaabe594
commit 49c3c9bec1
5 changed files with 19 additions and 9 deletions

View file

@ -117,14 +117,17 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
return $qb;
}
public function findOneByShortCode(string $shortCode): ?ShortUrl
public function findOneByShortCode(string $shortCode, ?string $domain = null): ?ShortUrl
{
$dql= <<<DQL
SELECT s
FROM Shlinkio\Shlink\Core\Entity\ShortUrl AS s
LEFT JOIN s.domain AS d
WHERE s.shortCode = :shortCode
AND (s.validSince <= :now OR s.validSince IS NULL)
AND (s.validUntil >= :now OR s.validUntil IS NULL)
AND (s.domain IS NULL OR d.authority = :domain)
ORDER BY s.domain DESC
DQL;
$query = $this->getEntityManager()->createQuery($dql);
@ -132,11 +135,18 @@ DQL;
->setParameters([
'shortCode' => $shortCode,
'now' => Chronos::now(),
'domain' => $domain,
]);
/** @var ShortUrl|null $result */
$result = $query->getOneOrNullResult();
return $result === null || $result->maxVisitsReached() ? null : $result;
// Since we ordered by domain DESC, we will have first the URL matching the domain, followed
// by the one with no domain (if any), so it is safe to fetch 1 max result and we will get:
// * The short URL matching both the short code and the domain, or
// * The short URL matching the short code but without any domain, or
// * No short URL at all
/** @var ShortUrl|null $shortUrl */
$shortUrl = $query->getOneOrNullResult();
return $shortUrl !== null && ! $shortUrl->maxVisitsReached() ? $shortUrl : null;
}
public function slugIsInUse(string $slug, ?string $domain = null): bool

View file

@ -26,7 +26,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository
*/
public function countList(?string $searchTerm = null, array $tags = []): int;
public function findOneByShortCode(string $shortCode): ?ShortUrl;
public function findOneByShortCode(string $shortCode, ?string $domain = null): ?ShortUrl;
public function slugIsInUse(string $slug, ?string $domain): bool;
}

View file

@ -175,7 +175,7 @@ class UrlShortener implements UrlShortenerInterface
* @throws InvalidShortCodeException
* @throws EntityDoesNotExistException
*/
public function shortCodeToUrl(string $shortCode): ShortUrl
public function shortCodeToUrl(string $shortCode, ?string $domain = null): ShortUrl
{
$chars = $this->options->getChars();
@ -186,7 +186,7 @@ class UrlShortener implements UrlShortenerInterface
/** @var ShortUrlRepository $shortUrlRepo */
$shortUrlRepo = $this->em->getRepository(ShortUrl::class);
$shortUrl = $shortUrlRepo->findOneByShortCode($shortCode);
$shortUrl = $shortUrlRepo->findOneByShortCode($shortCode, $domain);
if ($shortUrl === null) {
throw EntityDoesNotExistException::createFromEntityAndConditions(ShortUrl::class, [
'shortCode' => $shortCode,

View file

@ -26,5 +26,5 @@ interface UrlShortenerInterface
* @throws InvalidShortCodeException
* @throws EntityDoesNotExistException
*/
public function shortCodeToUrl(string $shortCode): ShortUrl;
public function shortCodeToUrl(string $shortCode, ?string $domain = null): ShortUrl;
}

View file

@ -247,7 +247,7 @@ class UrlShortenerTest extends TestCase
$shortUrl->setShortCode($shortCode);
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$repo->findOneByShortCode($shortCode)->willReturn($shortUrl);
$repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$url = $this->urlShortener->shortCodeToUrl($shortCode);