diff --git a/data/migrations/Version20210207100807.php b/data/migrations/Version20210207100807.php new file mode 100644 index 00000000..a74c0b08 --- /dev/null +++ b/data/migrations/Version20210207100807.php @@ -0,0 +1,53 @@ +getTable('visits'); + $shortUrlId = $visits->getColumn('short_url_id'); + + $this->skipIf(! $shortUrlId->getNotnull()); + + $shortUrlId->setNotnull(false); + + $visits->addColumn('visited_url', Types::STRING, [ + 'length' => Visitor::VISITED_URL_MAX_LENGTH, + 'notnull' => false, + ]); + $visits->addColumn('type', Types::STRING, [ + 'length' => 255, + 'default' => Visit::TYPE_VALID_SHORT_URL, + ]); + } + + public function down(Schema $schema): void + { + $visits = $schema->getTable('visits'); + $shortUrlId = $visits->getColumn('short_url_id'); + + $this->skipIf($shortUrlId->getNotnull()); + + $shortUrlId->setNotnull(true); + $visits->dropColumn('visited_url'); + $visits->dropColumn('type'); + } + + /** + * @fixme Workaround for https://github.com/doctrine/migrations/issues/1104 + */ + public function isTransactional(): bool + { + return false; + } +} diff --git a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php index 5143389b..efcccb65 100644 --- a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php +++ b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php @@ -47,11 +47,22 @@ return static function (ClassMetadata $metadata, array $emConfig): void { ->build(); $builder->createManyToOne('shortUrl', Entity\ShortUrl::class) - ->addJoinColumn('short_url_id', 'id', false, false, 'CASCADE') + ->addJoinColumn('short_url_id', 'id', true, false, 'CASCADE') ->build(); $builder->createManyToOne('visitLocation', Entity\VisitLocation::class) ->addJoinColumn('visit_location_id', 'id', true, false, 'Set NULL') ->cascadePersist() ->build(); + + $builder->createField('visitedUrl', Types::STRING) + ->columnName('visited_url') + ->length(Visitor::VISITED_URL_MAX_LENGTH) + ->nullable() + ->build(); + + $builder->createField('type', Types::STRING) + ->columnName('type') + ->length(255) + ->build(); }; diff --git a/module/Core/src/Entity/Visit.php b/module/Core/src/Entity/Visit.php index c2b79a04..5b1b91d3 100644 --- a/module/Core/src/Entity/Visit.php +++ b/module/Core/src/Entity/Visit.php @@ -14,10 +14,17 @@ use Shlinkio\Shlink\Core\Visit\Model\VisitLocationInterface; class Visit extends AbstractEntity implements JsonSerializable { + public const TYPE_VALID_SHORT_URL = 'valid_short_url'; + public const TYPE_INVALID_SHORT_URL = 'invalid_short_url'; + public const TYPE_BASE_URL = 'base_url'; + public const TYPE_REGULAR_404 = 'regular_404'; + private string $referer; private Chronos $date; private ?string $remoteAddr = null; + private ?string $visitedUrl = null; private string $userAgent; + private string $type; private ?ShortUrl $shortUrl; private ?VisitLocation $visitLocation = null; @@ -28,6 +35,7 @@ class Visit extends AbstractEntity implements JsonSerializable $this->userAgent = $visitor->getUserAgent(); $this->referer = $visitor->getReferer(); $this->remoteAddr = $this->processAddress($anonymize, $visitor->getRemoteAddress()); + $this->type = self::TYPE_VALID_SHORT_URL; } private function processAddress(bool $anonymize, ?string $address): ?string diff --git a/module/Core/src/Model/Visitor.php b/module/Core/src/Model/Visitor.php index 8c24ab26..f973be49 100644 --- a/module/Core/src/Model/Visitor.php +++ b/module/Core/src/Model/Visitor.php @@ -14,6 +14,7 @@ final class Visitor public const USER_AGENT_MAX_LENGTH = 512; public const REFERER_MAX_LENGTH = 1024; public const REMOTE_ADDRESS_MAX_LENGTH = 256; + public const VISITED_URL_MAX_LENGTH = 2048; private string $userAgent; private string $referer;