diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index 10df9352..5453d791 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -12,6 +12,7 @@ use Shlinkio\Shlink\Common\Entity\AbstractEntity; use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver; use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException; +use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use function array_reduce; @@ -93,16 +94,19 @@ class ShortUrl extends AbstractEntity return $this; } - public function updateMeta(ShortUrlMeta $shortCodeMeta): void + public function update(ShortUrlEdit $shortUrlEdit): void { - if ($shortCodeMeta->hasValidSince()) { - $this->validSince = $shortCodeMeta->getValidSince(); + if ($shortUrlEdit->hasValidSince()) { + $this->validSince = $shortUrlEdit->validSince(); } - if ($shortCodeMeta->hasValidUntil()) { - $this->validUntil = $shortCodeMeta->getValidUntil(); + if ($shortUrlEdit->hasValidUntil()) { + $this->validUntil = $shortUrlEdit->validUntil(); } - if ($shortCodeMeta->hasMaxVisits()) { - $this->maxVisits = $shortCodeMeta->getMaxVisits(); + if ($shortUrlEdit->hasMaxVisits()) { + $this->maxVisits = $shortUrlEdit->maxVisits(); + } + if ($shortUrlEdit->hasLongUrl()) { + $this->longUrl = $shortUrlEdit->longUrl(); } } diff --git a/module/Core/src/Model/ShortUrlEdit.php b/module/Core/src/Model/ShortUrlEdit.php new file mode 100644 index 00000000..2f3f6919 --- /dev/null +++ b/module/Core/src/Model/ShortUrlEdit.php @@ -0,0 +1,106 @@ +validateAndInit($data); + return $instance; + } + + /** + * @throws ValidationException + */ + private function validateAndInit(array $data): void + { + $inputFilter = new ShortUrlMetaInputFilter($data); + if (! $inputFilter->isValid()) { + throw ValidationException::fromInputFilter($inputFilter); + } + + $this->longUrlPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::LONG_URL, $data); + $this->validSincePropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_SINCE, $data); + $this->validUntilPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_UNTIL, $data); + $this->maxVisitsPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::MAX_VISITS, $data); + + $this->longUrl = $inputFilter->getValue(ShortUrlMetaInputFilter::LONG_URL); + $this->validSince = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE)); + $this->validUntil = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL)); + $this->maxVisits = $this->getOptionalIntFromInputFilter($inputFilter, ShortUrlMetaInputFilter::MAX_VISITS); + } + + private function getOptionalIntFromInputFilter(ShortUrlMetaInputFilter $inputFilter, string $fieldName): ?int + { + $value = $inputFilter->getValue($fieldName); + return $value !== null ? (int) $value : null; + } + + public function longUrl(): ?string + { + return $this->longUrl; + } + + public function hasLongUrl(): bool + { + return $this->longUrlPropWasProvided && $this->longUrl !== null; + } + + public function validSince(): ?Chronos + { + return $this->validSince; + } + + public function hasValidSince(): bool + { + return $this->validSincePropWasProvided; + } + + public function validUntil(): ?Chronos + { + return $this->validUntil; + } + + public function hasValidUntil(): bool + { + return $this->validUntilPropWasProvided; + } + + public function maxVisits(): ?int + { + return $this->maxVisits; + } + + public function hasMaxVisits(): bool + { + return $this->maxVisitsPropWasProvided; + } +} diff --git a/module/Core/src/Model/ShortUrlMeta.php b/module/Core/src/Model/ShortUrlMeta.php index 3bba5c98..76f6d80b 100644 --- a/module/Core/src/Model/ShortUrlMeta.php +++ b/module/Core/src/Model/ShortUrlMeta.php @@ -8,25 +8,21 @@ use Cake\Chronos\Chronos; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter; -use function array_key_exists; use function Shlinkio\Shlink\Core\parseDateField; use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH; final class ShortUrlMeta { - private bool $validSincePropWasProvided = false; private ?Chronos $validSince = null; - private bool $validUntilPropWasProvided = false; private ?Chronos $validUntil = null; private ?string $customSlug = null; - private bool $maxVisitsPropWasProvided = false; private ?int $maxVisits = null; private ?bool $findIfExists = null; private ?string $domain = null; private int $shortCodeLength = 5; - // Force named constructors + // Enforce named constructors private function __construct() { } @@ -57,12 +53,9 @@ final class ShortUrlMeta } $this->validSince = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE)); - $this->validSincePropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_SINCE, $data); $this->validUntil = parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL)); - $this->validUntilPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_UNTIL, $data); $this->customSlug = $inputFilter->getValue(ShortUrlMetaInputFilter::CUSTOM_SLUG); $this->maxVisits = $this->getOptionalIntFromInputFilter($inputFilter, ShortUrlMetaInputFilter::MAX_VISITS); - $this->maxVisitsPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::MAX_VISITS, $data); $this->findIfExists = $inputFilter->getValue(ShortUrlMetaInputFilter::FIND_IF_EXISTS); $this->domain = $inputFilter->getValue(ShortUrlMetaInputFilter::DOMAIN); $this->shortCodeLength = $this->getOptionalIntFromInputFilter( @@ -84,7 +77,7 @@ final class ShortUrlMeta public function hasValidSince(): bool { - return $this->validSincePropWasProvided; + return $this->validSince !== null; } public function getValidUntil(): ?Chronos @@ -94,7 +87,7 @@ final class ShortUrlMeta public function hasValidUntil(): bool { - return $this->validUntilPropWasProvided; + return $this->validUntil !== null; } public function getCustomSlug(): ?string @@ -114,7 +107,7 @@ final class ShortUrlMeta public function hasMaxVisits(): bool { - return $this->maxVisitsPropWasProvided; + return $this->maxVisits !== null; } public function findIfExists(): bool diff --git a/module/Core/src/Service/ShortUrlService.php b/module/Core/src/Service/ShortUrlService.php index e9aaf637..ae0cc7a3 100644 --- a/module/Core/src/Service/ShortUrlService.php +++ b/module/Core/src/Service/ShortUrlService.php @@ -8,8 +8,8 @@ use Doctrine\ORM; use Laminas\Paginator\Paginator; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; +use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; -use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Paginator\Adapter\ShortUrlRepositoryAdapter; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; @@ -60,10 +60,10 @@ class ShortUrlService implements ShortUrlServiceInterface /** * @throws ShortUrlNotFoundException */ - public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlMeta $shortUrlMeta): ShortUrl + public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlEdit $shortUrlEdit): ShortUrl { $shortUrl = $this->urlResolver->resolveShortUrl($identifier); - $shortUrl->updateMeta($shortUrlMeta); + $shortUrl->update($shortUrlEdit); $this->em->flush(); diff --git a/module/Core/src/Service/ShortUrlServiceInterface.php b/module/Core/src/Service/ShortUrlServiceInterface.php index 379abc55..f17a7bea 100644 --- a/module/Core/src/Service/ShortUrlServiceInterface.php +++ b/module/Core/src/Service/ShortUrlServiceInterface.php @@ -7,8 +7,8 @@ namespace Shlinkio\Shlink\Core\Service; use Laminas\Paginator\Paginator; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; +use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; -use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; interface ShortUrlServiceInterface @@ -27,5 +27,5 @@ interface ShortUrlServiceInterface /** * @throws ShortUrlNotFoundException */ - public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlMeta $shortUrlMeta): ShortUrl; + public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlEdit $shortUrlEdit): ShortUrl; } diff --git a/module/Core/src/Validation/ShortUrlMetaInputFilter.php b/module/Core/src/Validation/ShortUrlMetaInputFilter.php index 013edd8f..8fde5e98 100644 --- a/module/Core/src/Validation/ShortUrlMetaInputFilter.php +++ b/module/Core/src/Validation/ShortUrlMetaInputFilter.php @@ -23,6 +23,7 @@ class ShortUrlMetaInputFilter extends InputFilter public const FIND_IF_EXISTS = 'findIfExists'; public const DOMAIN = 'domain'; public const SHORT_CODE_LENGTH = 'shortCodeLength'; + public const LONG_URL = 'longUrl'; public function __construct(array $data) { @@ -32,6 +33,8 @@ class ShortUrlMetaInputFilter extends InputFilter private function initialize(): void { + $this->add($this->createInput(self::LONG_URL, false)); + $validSince = $this->createInput(self::VALID_SINCE, false); $validSince->getValidatorChain()->attach(new Validator\Date(['format' => DateTime::ATOM])); $this->add($validSince); diff --git a/module/Core/test/Service/ShortUrlServiceTest.php b/module/Core/test/Service/ShortUrlServiceTest.php index 842eac60..4f40683e 100644 --- a/module/Core/test/Service/ShortUrlServiceTest.php +++ b/module/Core/test/Service/ShortUrlServiceTest.php @@ -12,8 +12,8 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\Tag; +use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; -use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; @@ -82,7 +82,7 @@ class ShortUrlServiceTest extends TestCase $findShortUrl = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier('abc123'))->willReturn($shortUrl); $flush = $this->em->flush()->willReturn(null); - $result = $this->service->updateMetadataByShortCode(new ShortUrlIdentifier('abc123'), ShortUrlMeta::fromRawData( + $result = $this->service->updateMetadataByShortCode(new ShortUrlIdentifier('abc123'), ShortUrlEdit::fromRawData( [ 'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(), 'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(), diff --git a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php index 8b3e65ab..da7012b6 100644 --- a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php @@ -8,8 +8,8 @@ use Laminas\Diactoros\Response\EmptyResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; +use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; -use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; @@ -28,10 +28,10 @@ class EditShortUrlAction extends AbstractRestAction public function handle(ServerRequestInterface $request): ResponseInterface { - $postData = (array) $request->getParsedBody(); + $shortUrlEdit = ShortUrlEdit::fromRawData((array) $request->getParsedBody()); $identifier = ShortUrlIdentifier::fromApiRequest($request); - $this->shortUrlService->updateMetadataByShortCode($identifier, ShortUrlMeta::fromRawData($postData)); + $this->shortUrlService->updateMetadataByShortCode($identifier, $shortUrlEdit); return new EmptyResponse(); } }