Moved short code uniqueness checks to external helper class that is used in UrlShortener and ImportedLinksProcessor

This commit is contained in:
Alejandro Celaya
2020-10-25 11:16:42 +01:00
parent b1a073b1ab
commit 786e4f642b
9 changed files with 180 additions and 82 deletions

View File

@@ -133,7 +133,7 @@ class ShortUrl extends AbstractEntity
/**
* @throws ShortCodeCannotBeRegeneratedException
*/
public function regenerateShortCode(): self
public function regenerateShortCode(): void
{
// In ShortUrls where a custom slug was provided, throw error, unless it is an imported one
if ($this->customSlugWasProvided && $this->importSource === null) {
@@ -146,7 +146,6 @@ class ShortUrl extends AbstractEntity
}
$this->shortCode = generateRandomShortCode($this->shortCodeLength);
return $this;
}
public function getValidSince(): ?Chronos

View File

@@ -7,8 +7,8 @@ namespace Shlinkio\Shlink\Core\Importer;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface;
use Shlinkio\Shlink\Core\Util\DoctrineBatchIterator;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface;
@@ -23,11 +23,16 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
private EntityManagerInterface $em;
private DomainResolverInterface $domainResolver;
private ShortCodeHelperInterface $shortCodeHelper;
public function __construct(EntityManagerInterface $em, DomainResolverInterface $domainResolver)
{
public function __construct(
EntityManagerInterface $em,
DomainResolverInterface $domainResolver,
ShortCodeHelperInterface $shortCodeHelper
) {
$this->em = $em;
$this->domainResolver = $domainResolver;
$this->shortCodeHelper = $shortCodeHelper;
}
/**
@@ -68,7 +73,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
StyleInterface $io,
bool $importShortCodes
): bool {
if ($this->ensureShortCodeUniqueness($shortUrl, $importShortCodes)) {
if ($this->shortCodeHelper->ensureShortCodeUniqueness($shortUrl, $importShortCodes)) {
return true;
}
@@ -87,26 +92,4 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
return $this->handleShortcodeUniqueness($url, $shortUrl, $io, false);
}
private function ensureShortCodeUniqueness(ShortUrl $shortUrlToBeCreated, bool $hasCustomSlug): bool
{
$shortCode = $shortUrlToBeCreated->getShortCode();
$domain = $shortUrlToBeCreated->getDomain();
$domainAuthority = $domain !== null ? $domain->getAuthority() : null;
/** @var ShortUrlRepository $repo */
$repo = $this->em->getRepository(ShortUrl::class);
$otherShortUrlsExist = $repo->shortCodeIsInUse($shortCode, $domainAuthority);
if (! $otherShortUrlsExist) {
return true;
}
if ($hasCustomSlug) {
return false;
}
$shortUrlToBeCreated->regenerateShortCode();
return $this->ensureShortCodeUniqueness($shortUrlToBeCreated, $hasCustomSlug);
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service\ShortUrl;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
class ShortCodeHelper implements ShortCodeHelperInterface
{
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function ensureShortCodeUniqueness(ShortUrl $shortUrlToBeCreated, bool $hasCustomSlug): bool
{
$shortCode = $shortUrlToBeCreated->getShortCode();
$domain = $shortUrlToBeCreated->getDomain();
$domainAuthority = $domain !== null ? $domain->getAuthority() : null;
/** @var ShortUrlRepository $repo */
$repo = $this->em->getRepository(ShortUrl::class);
$otherShortUrlsExist = $repo->shortCodeIsInUse($shortCode, $domainAuthority);
if (! $otherShortUrlsExist) {
return true;
}
if ($hasCustomSlug) {
return false;
}
$shortUrlToBeCreated->regenerateShortCode();
return $this->ensureShortCodeUniqueness($shortUrlToBeCreated, $hasCustomSlug);
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service\ShortUrl;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
interface ShortCodeHelperInterface
{
public function ensureShortCodeUniqueness(ShortUrl $shortUrlToBeCreated, bool $hasCustomSlug): bool;
}

View File

@@ -10,8 +10,8 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Shlinkio\Shlink\Core\Util\UrlValidatorInterface;
use Throwable;
@@ -23,15 +23,18 @@ class UrlShortener implements UrlShortenerInterface
private EntityManagerInterface $em;
private UrlValidatorInterface $urlValidator;
private DomainResolverInterface $domainResolver;
private ShortCodeHelperInterface $shortCodeHelper;
public function __construct(
UrlValidatorInterface $urlValidator,
EntityManagerInterface $em,
DomainResolverInterface $domainResolver
DomainResolverInterface $domainResolver,
ShortCodeHelperInterface $shortCodeHelper
) {
$this->urlValidator = $urlValidator;
$this->em = $em;
$this->domainResolver = $domainResolver;
$this->shortCodeHelper = $shortCodeHelper;
}
/**
@@ -83,20 +86,16 @@ class UrlShortener implements UrlShortenerInterface
private function verifyShortCodeUniqueness(ShortUrlMeta $meta, ShortUrl $shortUrlToBeCreated): void
{
$shortCode = $shortUrlToBeCreated->getShortCode();
$domain = $meta->getDomain();
$couldBeMadeUnique = $this->shortCodeHelper->ensureShortCodeUniqueness(
$shortUrlToBeCreated,
$meta->hasCustomSlug(),
);
/** @var ShortUrlRepository $repo */
$repo = $this->em->getRepository(ShortUrl::class);
$otherShortUrlsExist = $repo->shortCodeIsInUse($shortCode, $domain);
if (! $couldBeMadeUnique) {
$domain = $shortUrlToBeCreated->getDomain();
$domainAuthority = $domain !== null ? $domain->getAuthority() : null;
if ($otherShortUrlsExist && $meta->hasCustomSlug()) {
throw NonUniqueSlugException::fromSlug($shortCode, $domain);
}
if ($otherShortUrlsExist) {
$shortUrlToBeCreated->regenerateShortCode();
$this->verifyShortCodeUniqueness($meta, $shortUrlToBeCreated);
throw NonUniqueSlugException::fromSlug($shortUrlToBeCreated->getShortCode(), $domainAuthority);
}
}
}