From af7c11665c64cd06686c157132f9a253747ccf70 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 22 Oct 2017 09:00:32 +0200 Subject: [PATCH] Added max_visits field to short_urls --- data/migrations/Version20171022064541.php | 38 +++++++++++++++++++ module/Core/src/Entity/ShortUrl.php | 37 +++++++++++++++++- module/Core/src/Service/VisitsTracker.php | 19 ++++++---- .../src/Service/VisitsTrackerInterface.php | 5 ++- .../Core/test/Service/VisitsTrackerTest.php | 4 +- 5 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 data/migrations/Version20171022064541.php diff --git a/data/migrations/Version20171022064541.php b/data/migrations/Version20171022064541.php new file mode 100644 index 00000000..bac05c61 --- /dev/null +++ b/data/migrations/Version20171022064541.php @@ -0,0 +1,38 @@ +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'); + } +} diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index 0f7ea621..6c9b5189 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -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(), ]; } diff --git a/module/Core/src/Service/VisitsTracker.php b/module/Core/src/Service/VisitsTracker.php index 360b0d2b..38e44fea 100644 --- a/module/Core/src/Service/VisitsTracker.php +++ b/module/Core/src/Service/VisitsTracker.php @@ -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)); } diff --git a/module/Core/src/Service/VisitsTrackerInterface.php b/module/Core/src/Service/VisitsTrackerInterface.php index 8dfa6145..d3295f37 100644 --- a/module/Core/src/Service/VisitsTrackerInterface.php +++ b/module/Core/src/Service/VisitsTrackerInterface.php @@ -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; } diff --git a/module/Core/test/Service/VisitsTrackerTest.php b/module/Core/test/Service/VisitsTrackerTest.php index a3d475c4..f931c4e4 100644 --- a/module/Core/test/Service/VisitsTrackerTest.php +++ b/module/Core/test/Service/VisitsTrackerTest.php @@ -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']