mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-07 07:43:12 +08:00
Add two modes for short URLs
This commit is contained in:
@@ -43,6 +43,7 @@ enum EnvVars: string
|
||||
case REDIRECT_CACHE_LIFETIME = 'REDIRECT_CACHE_LIFETIME';
|
||||
case BASE_PATH = 'BASE_PATH';
|
||||
case SHORT_URL_TRAILING_SLASH = 'SHORT_URL_TRAILING_SLASH';
|
||||
case SHORT_URL_MODE = 'SHORT_URL_MODE';
|
||||
case PORT = 'PORT';
|
||||
case TASK_WORKER_NUM = 'TASK_WORKER_NUM';
|
||||
case WEB_WORKER_NUM = 'WEB_WORKER_NUM';
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Options;
|
||||
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
|
||||
final class UrlShortenerOptions
|
||||
@@ -16,6 +18,7 @@ final class UrlShortenerOptions
|
||||
public readonly bool $appendExtraPath = false,
|
||||
public readonly bool $multiSegmentSlugsEnabled = false,
|
||||
public readonly bool $trailingSlashEnabled = false,
|
||||
public readonly ShortUrlMode $mode = ShortUrlMode::STRICT,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\DeviceLongUrlPair;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlEdition;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\SimpleShortUrlRelationResolver;
|
||||
@@ -95,7 +96,10 @@ class ShortUrl extends AbstractEntity
|
||||
$instance->maxVisits = $creation->maxVisits;
|
||||
$instance->customSlugWasProvided = $creation->hasCustomSlug();
|
||||
$instance->shortCodeLength = $creation->shortCodeLength;
|
||||
$instance->shortCode = $creation->customSlug ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->shortCode = $creation->customSlug ?? generateRandomShortCode(
|
||||
$instance->shortCodeLength,
|
||||
$creation->shortUrlMode,
|
||||
);
|
||||
$instance->domain = $relationResolver->resolveDomain($creation->domain);
|
||||
$instance->authorApiKey = $creation->apiKey;
|
||||
$instance->title = $creation->title;
|
||||
@@ -292,7 +296,7 @@ class ShortUrl extends AbstractEntity
|
||||
/**
|
||||
* @throws ShortCodeCannotBeRegeneratedException
|
||||
*/
|
||||
public function regenerateShortCode(): void
|
||||
public function regenerateShortCode(ShortUrlMode $mode): void
|
||||
{
|
||||
// In ShortUrls where a custom slug was provided, throw error, unless it is an imported one
|
||||
if ($this->customSlugWasProvided && $this->importSource === null) {
|
||||
@@ -304,7 +308,7 @@ class ShortUrl extends AbstractEntity
|
||||
throw ShortCodeCannotBeRegeneratedException::forShortUrlAlreadyPersisted();
|
||||
}
|
||||
|
||||
$this->shortCode = generateRandomShortCode($this->shortCodeLength);
|
||||
$this->shortCode = generateRandomShortCode($this->shortCodeLength, $mode);
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
|
||||
@@ -5,14 +5,17 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\Core\ShortUrl\Helper;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository;
|
||||
|
||||
class ShortCodeUniquenessHelper implements ShortCodeUniquenessHelperInterface
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $em)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly UrlShortenerOptions $options,
|
||||
) {
|
||||
}
|
||||
|
||||
public function ensureShortCodeUniqueness(ShortUrl $shortUrlToBeCreated, bool $hasCustomSlug): bool
|
||||
@@ -29,7 +32,7 @@ class ShortCodeUniquenessHelper implements ShortCodeUniquenessHelperInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
$shortUrlToBeCreated->regenerateShortCode();
|
||||
$shortUrlToBeCreated->regenerateShortCode($this->options->mode);
|
||||
return $this->ensureShortCodeUniqueness($shortUrlToBeCreated, $hasCustomSlug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ final class ShortUrlCreation implements TitleResolutionModelInterface
|
||||
*/
|
||||
private function __construct(
|
||||
public readonly string $longUrl,
|
||||
public readonly ShortUrlMode $shortUrlMode,
|
||||
public readonly array $deviceLongUrls = [],
|
||||
public readonly ?Chronos $validSince = null,
|
||||
public readonly ?Chronos $validUntil = null,
|
||||
@@ -47,7 +48,7 @@ final class ShortUrlCreation implements TitleResolutionModelInterface
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function fromRawData(array $data): self
|
||||
public static function fromRawData(array $data, ShortUrlMode $mode = ShortUrlMode::STRICT): self
|
||||
{
|
||||
$inputFilter = ShortUrlInputFilter::withRequiredLongUrl($data);
|
||||
if (! $inputFilter->isValid()) {
|
||||
@@ -60,6 +61,7 @@ final class ShortUrlCreation implements TitleResolutionModelInterface
|
||||
|
||||
return new self(
|
||||
longUrl: $inputFilter->getValue(ShortUrlInputFilter::LONG_URL),
|
||||
shortUrlMode: $mode,
|
||||
deviceLongUrls: $deviceLongUrls,
|
||||
validSince: normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_SINCE)),
|
||||
validUntil: normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_UNTIL)),
|
||||
@@ -84,6 +86,7 @@ final class ShortUrlCreation implements TitleResolutionModelInterface
|
||||
{
|
||||
return new self(
|
||||
longUrl: $this->longUrl,
|
||||
shortUrlMode: $this->shortUrlMode,
|
||||
deviceLongUrls: $this->deviceLongUrls,
|
||||
validSince: $this->validSince,
|
||||
validUntil: $this->validUntil,
|
||||
|
||||
9
module/Core/src/ShortUrl/Model/ShortUrlMode.php
Normal file
9
module/Core/src/ShortUrl/Model/ShortUrlMode.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ShortUrl\Model;
|
||||
|
||||
enum ShortUrlMode: string
|
||||
{
|
||||
case STRICT = 'strict';
|
||||
case LOOSELY = 'loosely';
|
||||
}
|
||||
@@ -42,7 +42,6 @@ class ShortUrlInputFilter extends InputFilter
|
||||
|
||||
private function __construct(array $data, bool $requireLongUrl)
|
||||
{
|
||||
// FIXME The multi-segment slug option should be injected
|
||||
$this->initialize($requireLongUrl, $data[EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value] ?? false);
|
||||
$this->setData($data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user