Created method to updated already created short URLs

This commit is contained in:
Alejandro Celaya 2018-01-07 19:51:25 +01:00
parent 3243ade4fd
commit fac9455a1e
8 changed files with 360 additions and 8 deletions

View file

@ -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"

View 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;
}
}

View 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}

View 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;
}
}

View 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);
}
}

View 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
{
}
}