From fac9455a1ee7cdea777bb728656709ee73506630 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 7 Jan 2018 19:51:25 +0100 Subject: [PATCH] Created method to updated already created short URLs --- composer.json | 1 + .../src/Exception/ValidationException.php | 76 ++++++++++ module/Core/src/Model/ShortCodeMeta.php | 139 ++++++++++++++++++ module/Core/src/Service/ShortUrlService.php | 51 ++++++- .../src/Service/ShortUrlServiceInterface.php | 11 +- .../Core/src/Validation/InputFactoryTrait.php | 20 +++ .../Validation/ShortUrlMetaInputFilter.php | 45 ++++++ .../Rest/src/Action/EditShortCodeAction.php | 25 ++++ 8 files changed, 360 insertions(+), 8 deletions(-) create mode 100644 module/Core/src/Exception/ValidationException.php create mode 100644 module/Core/src/Model/ShortCodeMeta.php create mode 100644 module/Core/src/Validation/InputFactoryTrait.php create mode 100644 module/Core/src/Validation/ShortUrlMetaInputFilter.php create mode 100644 module/Rest/src/Action/EditShortCodeAction.php diff --git a/composer.json b/composer.json index 5a36d42b..d961f5d5 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "zendframework/zend-expressive-helpers": "^4.2", "zendframework/zend-expressive-platesrenderer": "^1.3", "zendframework/zend-i18n": "^2.7", + "zendframework/zend-inputfilter": "^2.8", "zendframework/zend-paginator": "^2.6", "zendframework/zend-servicemanager": "^3.2", "zendframework/zend-stdlib": "^3.0" diff --git a/module/Core/src/Exception/ValidationException.php b/module/Core/src/Exception/ValidationException.php new file mode 100644 index 00000000..5c6a95bc --- /dev/null +++ b/module/Core/src/Exception/ValidationException.php @@ -0,0 +1,76 @@ +invalidElements = $invalidElements; + parent::__construct($message, $code, $previous); + } + + /** + * @param InputFilterInterface $inputFilter + * @param \Throwable|null $prev + * @return ValidationException + */ + public static function fromInputFilter(InputFilterInterface $inputFilter, \Throwable $prev = null): self + { + return static::fromArray($inputFilter->getMessages(), $prev); + } + + /** + * @param array $invalidData + * @param \Throwable|null $prev + * @return ValidationException + */ + public static function fromArray(array $invalidData, \Throwable $prev = null): self + { + return new self( + \sprintf( + 'Provided data is not valid. These are the messages:%s%s%s', + PHP_EOL, + self::formMessagesToString($invalidData), + PHP_EOL + ), + $invalidData, + -1, + $prev + ); + } + + private static function formMessagesToString(array $messages = []) + { + $text = ''; + foreach ($messages as $name => $messageSet) { + $text .= \sprintf( + "\n\t'%s' => %s", + $name, + \is_array($messageSet) ? \print_r($messageSet, true) : $messageSet + ); + } + + return $text; + } + + /** + * @return array + */ + public function getInvalidElements(): array + { + return $this->invalidElements; + } +} diff --git a/module/Core/src/Model/ShortCodeMeta.php b/module/Core/src/Model/ShortCodeMeta.php new file mode 100644 index 00000000..5bdcdf8a --- /dev/null +++ b/module/Core/src/Model/ShortCodeMeta.php @@ -0,0 +1,139 @@ +validate($data); + return $instance; + } + + /** + * @param string|\DateTimeInterface|null $validSince + * @param string|\DateTimeInterface|null $validUntil + * @param string|null $customSlug + * @param int|null $maxVisits + * @return ShortCodeMeta + * @throws ValidationException + */ + public static function createFromParams( + $validSince = null, + $validUntil = null, + $customSlug = null, + $maxVisits = null + ): self { + // We do not type hint the arguments because that will be done by the validation process + $instance = new self(); + $instance->validate([ + ShortUrlMetaInputFilter::VALID_SINCE => $validSince, + ShortUrlMetaInputFilter::VALID_UNTIL => $validUntil, + ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, + ShortUrlMetaInputFilter::MAX_VISITS => $maxVisits, + ]); + return $instance; + } + + /** + * @param array $data + * @throws ValidationException + */ + private function validate(array $data) + { + $inputFilter = new ShortUrlMetaInputFilter($data); + if (! $inputFilter->isValid()) { + throw ValidationException::fromInputFilter($inputFilter); + } + + $this->validSince = $inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE); + $this->validUntil = $inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL); + $this->customSlug = $inputFilter->getValue(ShortUrlMetaInputFilter::CUSTOM_SLUG); + $this->maxVisits = $inputFilter->getValue(ShortUrlMetaInputFilter::MAX_VISITS); + $this->maxVisits = $this->maxVisits !== null ? (int) $this->maxVisits : null; + } + + /** + * @return \DateTime|null + */ + public function getValidSince() + { + return $this->validSince; + } + + public function hasValidSince(): bool + { + return $this->validSince !== null; + } + + /** + * @return \DateTime|null + */ + public function getValidUntil() + { + return $this->validUntil; + } + + public function hasValidUntil(): bool + { + return $this->validUntil !== null; + } + + /** + * @return null|string + */ + public function getCustomSlug() + { + return $this->customSlug; + } + + public function hasCustomSlug(): bool + { + return $this->customSlug !== null; + } + + /** + * @return int|null + */ + public function getMaxVisits() + { + return $this->maxVisits; + } + + public function hasMaxVisits(): bool + { + return $this->maxVisits !== null; + } +} diff --git a/module/Core/src/Service/ShortUrlService.php b/module/Core/src/Service/ShortUrlService.php index 85359060..261cb961 100644 --- a/module/Core/src/Service/ShortUrlService.php +++ b/module/Core/src/Service/ShortUrlService.php @@ -3,10 +3,11 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Service; -use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM; use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; +use Shlinkio\Shlink\Core\Model\ShortCodeMeta; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; use Shlinkio\Shlink\Core\Util\TagManagerTrait; use Zend\Paginator\Paginator; @@ -16,11 +17,11 @@ class ShortUrlService implements ShortUrlServiceInterface use TagManagerTrait; /** - * @var EntityManagerInterface + * @var ORM\EntityManagerInterface */ private $em; - public function __construct(EntityManagerInterface $em) + public function __construct(ORM\EntityManagerInterface $em) { $this->em = $em; } @@ -49,7 +50,46 @@ class ShortUrlService implements ShortUrlServiceInterface * @return ShortUrl * @throws InvalidShortCodeException */ - public function setTagsByShortCode($shortCode, array $tags = []): ShortUrl + public function setTagsByShortCode(string $shortCode, array $tags = []): ShortUrl + { + $shortUrl = $this->findByShortCode($shortCode); + $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags)); + $this->em->flush(); + + return $shortUrl; + } + + /** + * @param string $shortCode + * @param ShortCodeMeta $shortCodeMeta + * @return ShortUrl + * @throws InvalidShortCodeException + */ + public function updateMetadataByShortCode(string $shortCode, ShortCodeMeta $shortCodeMeta): ShortUrl + { + $shortUrl = $this->findByShortCode($shortCode); + if ($shortCodeMeta->hasValidSince()) { + $shortUrl->setValidSince($shortCodeMeta->getValidSince()); + } + if ($shortCodeMeta->hasValidUntil()) { + $shortUrl->setValidUntil($shortCodeMeta->getValidUntil()); + } + if ($shortCodeMeta->hasMaxVisits()) { + $shortUrl->setMaxVisits($shortCodeMeta->getMaxVisits()); + } + + /** @var ORM\EntityManager $em */ + $em = $this->em; + $em->flush($shortUrl); + return $shortUrl; + } + + /** + * @param string $shortCode + * @return ShortUrl + * @throws InvalidShortCodeException + */ + private function findByShortCode(string $shortCode): ShortUrl { /** @var ShortUrl|null $shortUrl */ $shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([ @@ -59,9 +99,6 @@ class ShortUrlService implements ShortUrlServiceInterface throw InvalidShortCodeException::fromNotFoundShortCode($shortCode); } - $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags)); - $this->em->flush(); - return $shortUrl; } } diff --git a/module/Core/src/Service/ShortUrlServiceInterface.php b/module/Core/src/Service/ShortUrlServiceInterface.php index 1815c4de..6ac3a9b6 100644 --- a/module/Core/src/Service/ShortUrlServiceInterface.php +++ b/module/Core/src/Service/ShortUrlServiceInterface.php @@ -5,6 +5,7 @@ namespace Shlinkio\Shlink\Core\Service; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; +use Shlinkio\Shlink\Core\Model\ShortCodeMeta; use Zend\Paginator\Paginator; interface ShortUrlServiceInterface @@ -24,5 +25,13 @@ interface ShortUrlServiceInterface * @return ShortUrl * @throws InvalidShortCodeException */ - public function setTagsByShortCode($shortCode, array $tags = []): ShortUrl; + public function setTagsByShortCode(string $shortCode, array $tags = []): ShortUrl; + + /** + * @param string $shortCode + * @param ShortCodeMeta $shortCodeMeta + * @return ShortUrl + * @throws InvalidShortCodeException + */ + public function updateMetadataByShortCode(string $shortCode, ShortCodeMeta $shortCodeMeta): ShortUrl; } diff --git a/module/Core/src/Validation/InputFactoryTrait.php b/module/Core/src/Validation/InputFactoryTrait.php new file mode 100644 index 00000000..289c54e2 --- /dev/null +++ b/module/Core/src/Validation/InputFactoryTrait.php @@ -0,0 +1,20 @@ +setRequired($required) + ->getFilterChain()->attach(new StripTags()) + ->attach(new StringTrim()); + return $input; + } +} diff --git a/module/Core/src/Validation/ShortUrlMetaInputFilter.php b/module/Core/src/Validation/ShortUrlMetaInputFilter.php new file mode 100644 index 00000000..435ef441 --- /dev/null +++ b/module/Core/src/Validation/ShortUrlMetaInputFilter.php @@ -0,0 +1,45 @@ +initialize(); + if ($data !== null) { + $this->setData($data); + } + } + + private function initialize() + { + $validSince = $this->createInput(self::VALID_SINCE, false); + $validSince->getValidatorChain()->attach(new Date(['format' => \DateTime::ATOM])); + $this->add($validSince); + + $validUntil = $this->createInput(self::VALID_UNTIL, false); + $validUntil->getValidatorChain()->attach(new Date(['format' => \DateTime::ATOM])); + $this->add($validUntil); + + $this->add($this->createInput(self::CUSTOM_SLUG, false)); + + $maxVisits = $this->createInput(self::MAX_VISITS, false); + $maxVisits->getValidatorChain()->attach(new IsInt()) + ->attach(new GreaterThan(['min' => 1, 'inclusive' => true])); + $this->add($maxVisits); + } +} diff --git a/module/Rest/src/Action/EditShortCodeAction.php b/module/Rest/src/Action/EditShortCodeAction.php new file mode 100644 index 00000000..f7b8eb49 --- /dev/null +++ b/module/Rest/src/Action/EditShortCodeAction.php @@ -0,0 +1,25 @@ +