Added max_visits field to short_urls

This commit is contained in:
Alejandro Celaya 2017-10-22 09:00:32 +02:00
parent 7f4678261e
commit af7c11665c
5 changed files with 89 additions and 14 deletions

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Types\Type;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20171022064541 extends AbstractMigration
{
/**
* @param Schema $schema
* @throws SchemaException
*/
public function up(Schema $schema)
{
$shortUrls = $schema->getTable('short_urls');
$shortUrls->addColumn('max_visits', Type::INTEGER, [
'unsigned' => true,
'notnull' => false,
]);
}
/**
* @param Schema $schema
* @throws SchemaException
*/
public function down(Schema $schema)
{
$shortUrls = $schema->getTable('short_urls');
$shortUrls->dropColumn('max_visits');
}
}

View File

@ -64,6 +64,11 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* @ORM\Column(name="valid_until", type="datetime", nullable=true)
*/
protected $validUntil;
/**
* @var integer
* @ORM\Column(name="max_visits", type="integer", nullable=true)
*/
protected $maxVisits;
/**
* ShortUrl constructor.
@ -194,6 +199,34 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
return $this;
}
public function getVisitsCount(): int
{
return count($this->visits);
}
/**
* @return int
*/
public function getMaxVisits(): int
{
return $this->maxVisits;
}
/**
* @param int $maxVisits
* @return $this|self
*/
public function setMaxVisits(int $maxVisits): self
{
$this->maxVisits = $maxVisits;
return $this;
}
public function maxVisitsReached(): bool
{
return $this->maxVisits !== null && $this->maxVisits >= $this->getVisitsCount();
}
/**
* Specify data which should be serialized to JSON
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
@ -206,8 +239,8 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
return [
'shortCode' => $this->shortCode,
'originalUrl' => $this->originalUrl,
'dateCreated' => isset($this->dateCreated) ? $this->dateCreated->format(\DateTime::ATOM) : null,
'visitsCount' => count($this->visits),
'dateCreated' => $this->dateCreated !== null ? $this->dateCreated->format(\DateTime::ATOM) : null,
'visitsCount' => $this->getVisitsCount(),
'tags' => $this->tags->toArray(),
];
}

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
@ -14,7 +15,7 @@ use Shlinkio\Shlink\Core\Repository\VisitRepository;
class VisitsTracker implements VisitsTrackerInterface
{
/**
* @var EntityManagerInterface
* @var EntityManagerInterface|EntityManager
*/
private $em;
@ -41,24 +42,25 @@ class VisitsTracker implements VisitsTrackerInterface
->setUserAgent($request->getHeaderLine('User-Agent'))
->setReferer($request->getHeaderLine('Referer'))
->setRemoteAddr($this->findOutRemoteAddr($request));
$this->em->persist($visit);
$this->em->flush();
$this->em->flush($visit);
}
/**
* @param ServerRequestInterface $request
* @return string
* @return string|null
*/
protected function findOutRemoteAddr(ServerRequestInterface $request)
private function findOutRemoteAddr(ServerRequestInterface $request)
{
$forwardedFor = $request->getHeaderLine('X-Forwarded-For');
if (empty($forwardedFor)) {
$serverParams = $request->getServerParams();
return isset($serverParams['REMOTE_ADDR']) ? $serverParams['REMOTE_ADDR'] : null;
return $serverParams['REMOTE_ADDR'] ?? null;
}
$ips = explode(',', $forwardedFor);
return $ips[0];
return $ips[0] ?? null;
}
/**
@ -67,14 +69,15 @@ class VisitsTracker implements VisitsTrackerInterface
* @param $shortCode
* @param DateRange $dateRange
* @return Visit[]
* @throws InvalidArgumentException
*/
public function info($shortCode, DateRange $dateRange = null)
public function info($shortCode, DateRange $dateRange = null): array
{
/** @var ShortUrl $shortUrl */
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([
'shortCode' => $shortCode,
]);
if (! isset($shortUrl)) {
if ($shortUrl === null) {
throw new InvalidArgumentException(sprintf('Short code "%s" not found', $shortCode));
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\Visit;
@ -14,7 +15,6 @@ interface VisitsTrackerInterface
*
* @param string $shortCode
* @param ServerRequestInterface $request
* @return
*/
public function track($shortCode, ServerRequestInterface $request);
@ -24,6 +24,7 @@ interface VisitsTrackerInterface
* @param $shortCode
* @param DateRange $dateRange
* @return Visit[]
* @throws InvalidArgumentException
*/
public function info($shortCode, DateRange $dateRange = null);
public function info($shortCode, DateRange $dateRange = null): array;
}

View File

@ -42,7 +42,7 @@ class VisitsTrackerTest extends TestCase
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
$this->em->persist(Argument::any())->shouldBeCalledTimes(1);
$this->em->flush()->shouldBeCalledTimes(1);
$this->em->flush(Argument::type(Visit::class))->shouldBeCalledTimes(1);
$this->visitsTracker->track($shortCode, ServerRequestFactory::fromGlobals());
}
@ -63,7 +63,7 @@ class VisitsTrackerTest extends TestCase
$visit = $args[0];
$test->assertEquals('4.3.2.1', $visit->getRemoteAddr());
})->shouldBeCalledTimes(1);
$this->em->flush()->shouldBeCalledTimes(1);
$this->em->flush(Argument::type(Visit::class))->shouldBeCalledTimes(1);
$this->visitsTracker->track($shortCode, ServerRequestFactory::fromGlobals(
['REMOTE_ADDR' => '1.2.3.4']