mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
Created method to updated already created short URLs
This commit is contained in:
parent
3243ade4fd
commit
fac9455a1e
8 changed files with 360 additions and 8 deletions
|
@ -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"
|
||||
|
|
76
module/Core/src/Exception/ValidationException.php
Normal file
76
module/Core/src/Exception/ValidationException.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Exception;
|
||||
|
||||
use Zend\InputFilter\InputFilterInterface;
|
||||
|
||||
class ValidationException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $invalidElements;
|
||||
|
||||
public function __construct(
|
||||
string $message = '',
|
||||
array $invalidElements = [],
|
||||
int $code = 0,
|
||||
\Throwable $previous = null
|
||||
) {
|
||||
$this->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;
|
||||
}
|
||||
}
|
139
module/Core/src/Model/ShortCodeMeta.php
Normal file
139
module/Core/src/Model/ShortCodeMeta.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Model;
|
||||
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter;
|
||||
|
||||
final class ShortCodeMeta
|
||||
{
|
||||
/**
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
private $validSince;
|
||||
/**
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
private $validUntil;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $customSlug;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $maxVisits;
|
||||
|
||||
// Force named constructors
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return ShortCodeMeta
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function createFromRawData(array $data): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
20
module/Core/src/Validation/InputFactoryTrait.php
Normal file
20
module/Core/src/Validation/InputFactoryTrait.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Validation;
|
||||
|
||||
use Zend\Filter\StringTrim;
|
||||
use Zend\Filter\StripTags;
|
||||
use Zend\InputFilter\Input;
|
||||
|
||||
trait InputFactoryTrait
|
||||
{
|
||||
public function createInput($name, $required = true): Input
|
||||
{
|
||||
$input = new Input($name);
|
||||
$input->setRequired($required)
|
||||
->getFilterChain()->attach(new StripTags())
|
||||
->attach(new StringTrim());
|
||||
return $input;
|
||||
}
|
||||
}
|
45
module/Core/src/Validation/ShortUrlMetaInputFilter.php
Normal file
45
module/Core/src/Validation/ShortUrlMetaInputFilter.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Validation;
|
||||
|
||||
use Zend\I18n\Validator\IsInt;
|
||||
use Zend\InputFilter\InputFilter;
|
||||
use Zend\Validator\Date;
|
||||
use Zend\Validator\GreaterThan;
|
||||
|
||||
class ShortUrlMetaInputFilter extends InputFilter
|
||||
{
|
||||
use InputFactoryTrait;
|
||||
|
||||
const VALID_SINCE = 'validSince';
|
||||
const VALID_UNTIL = 'validUntil';
|
||||
const CUSTOM_SLUG = 'customSlug';
|
||||
const MAX_VISITS = 'maxVisits';
|
||||
|
||||
public function __construct(array $data = null)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
25
module/Rest/src/Action/EditShortCodeAction.php
Normal file
25
module/Rest/src/Action/EditShortCodeAction.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\Action;
|
||||
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class EditShortCodeAction extends AbstractRestAction
|
||||
{
|
||||
/**
|
||||
* Process an incoming server request and return a response, optionally delegating
|
||||
* to the next middleware component to create the response.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param DelegateInterface $delegate
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||
{
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue