1
0
Fork 0
mirror of https://github.com/shlinkio/shlink.git synced 2025-04-02 06:45:33 +03:00

Improved how existing imported short URLs are checked by tracking its original short code

This commit is contained in:
Alejandro Celaya 2020-10-25 11:57:26 +01:00
parent 786e4f642b
commit 7c343f42c1
7 changed files with 49 additions and 21 deletions

View file

@ -53,7 +53,7 @@
"shlinkio/shlink-common": "^3.2.0", "shlinkio/shlink-common": "^3.2.0",
"shlinkio/shlink-config": "^1.0", "shlinkio/shlink-config": "^1.0",
"shlinkio/shlink-event-dispatcher": "^1.4", "shlinkio/shlink-event-dispatcher": "^1.4",
"shlinkio/shlink-importer": "^2.0", "shlinkio/shlink-importer": "^2.0.1",
"shlinkio/shlink-installer": "^5.1.0", "shlinkio/shlink-installer": "^5.1.0",
"shlinkio/shlink-ip-geolocation": "^1.5", "shlinkio/shlink-ip-geolocation": "^1.5",
"symfony/console": "^5.1", "symfony/console": "^5.1",

View file

@ -21,6 +21,15 @@ final class Version20201023090929 extends AbstractMigration
'length' => 255, 'length' => 255,
'notnull' => false, 'notnull' => false,
]); ]);
$shortUrls->addColumn('import_original_short_code', Types::STRING, [
'length' => 255,
'notnull' => false,
]);
$shortUrls->addUniqueIndex(
[self::IMPORT_SOURCE_COLUMN, 'import_original_short_code', 'domain_id'],
'unique_imports',
);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
@ -29,5 +38,7 @@ final class Version20201023090929 extends AbstractMigration
$this->skipIf(! $shortUrls->hasColumn(self::IMPORT_SOURCE_COLUMN)); $this->skipIf(! $shortUrls->hasColumn(self::IMPORT_SOURCE_COLUMN));
$shortUrls->dropColumn(self::IMPORT_SOURCE_COLUMN); $shortUrls->dropColumn(self::IMPORT_SOURCE_COLUMN);
$shortUrls->dropColumn('import_original_short_code');
$shortUrls->dropIndex('unique_imports');
} }
} }

View file

@ -56,6 +56,11 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
->nullable() ->nullable()
->build(); ->build();
$builder->createField('importOriginalShortCode', Types::STRING)
->columnName('import_original_short_code')
->nullable()
->build();
$builder->createOneToMany('visits', Entity\Visit::class) $builder->createOneToMany('visits', Entity\Visit::class)
->mappedBy('shortUrl') ->mappedBy('shortUrl')
->fetchExtraLazy() ->fetchExtraLazy()

View file

@ -36,6 +36,7 @@ class ShortUrl extends AbstractEntity
private bool $customSlugWasProvided; private bool $customSlugWasProvided;
private int $shortCodeLength; private int $shortCodeLength;
private ?string $importSource = null; private ?string $importSource = null;
private ?string $importOriginalShortCode = null;
public function __construct( public function __construct(
string $longUrl, string $longUrl,
@ -72,6 +73,7 @@ class ShortUrl extends AbstractEntity
$instance = new self($url->longUrl(), ShortUrlMeta::fromRawData($meta), $domainResolver); $instance = new self($url->longUrl(), ShortUrlMeta::fromRawData($meta), $domainResolver);
$instance->importSource = $url->source(); $instance->importSource = $url->source();
$instance->importOriginalShortCode = $url->shortCode();
$instance->dateCreated = Chronos::instance($url->createdAt()); $instance->dateCreated = Chronos::instance($url->createdAt());
return $instance; return $instance;

View file

@ -50,7 +50,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
$longUrl = $url->longUrl(); $longUrl = $url->longUrl();
// Skip already imported URLs // Skip already imported URLs
if ($shortUrlRepo->importedUrlExists($url, $importShortCodes)) { if ($shortUrlRepo->importedUrlExists($url)) {
$io->text(sprintf('%s: <comment>Skipped</comment>', $longUrl)); $io->text(sprintf('%s: <comment>Skipped</comment>', $longUrl));
continue; continue;
} }
@ -58,7 +58,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
$shortUrl = ShortUrl::fromImport($url, $importShortCodes, $this->domainResolver); $shortUrl = ShortUrl::fromImport($url, $importShortCodes, $this->domainResolver);
$shortUrl->setTags($this->tagNamesToEntities($this->em, $url->tags())); $shortUrl->setTags($this->tagNamesToEntities($this->em, $url->tags()));
if (! $this->handleShortcodeUniqueness($url, $shortUrl, $io, $importShortCodes)) { if (! $this->handleShortCodeUniqueness($url, $shortUrl, $io, $importShortCodes)) {
continue; continue;
} }
@ -67,7 +67,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
} }
} }
private function handleShortcodeUniqueness( private function handleShortCodeUniqueness(
ImportedShlinkUrl $url, ImportedShlinkUrl $url,
ShortUrl $shortUrl, ShortUrl $shortUrl,
StyleInterface $io, StyleInterface $io,
@ -90,6 +90,6 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
return false; return false;
} }
return $this->handleShortcodeUniqueness($url, $shortUrl, $io, false); return $this->handleShortCodeUniqueness($url, $shortUrl, $io, false);
} }
} }

View file

@ -190,13 +190,7 @@ DQL;
->setParameter('slug', $slug) ->setParameter('slug', $slug)
->setMaxResults(1); ->setMaxResults(1);
if ($domain !== null) { $this->whereDomainIs($qb, $domain);
$qb->join('s.domain', 'd')
->andWhere($qb->expr()->eq('d.authority', ':authority'))
->setParameter('authority', $domain);
} else {
$qb->andWhere($qb->expr()->isNull('s.domain'));
}
return $qb; return $qb;
} }
@ -256,15 +250,31 @@ DQL;
return $qb->getQuery()->getOneOrNullResult(); return $qb->getQuery()->getOneOrNullResult();
} }
public function importedUrlExists(ImportedShlinkUrl $url, bool $importShortCodes): bool public function importedUrlExists(ImportedShlinkUrl $url): bool
{ {
$findConditions = ['importSource' => $url->source()]; $qb = $this->getEntityManager()->createQueryBuilder();
if ($importShortCodes) { $qb->select('COUNT(DISTINCT s.id)')
$findConditions['shortCode'] = $url->shortCode(); ->from(ShortUrl::class, 's')
} else { ->andWhere($qb->expr()->eq('s.importOriginalShortCode', ':shortCode'))
$findConditions['longUrl'] = $url->longUrl(); ->setParameter('shortCode', $url->shortCode())
} ->andWhere($qb->expr()->eq('s.importSource', ':importSource'))
->setParameter('importSource', $url->source())
->setMaxResults(1);
return $this->count($findConditions) > 0; $this->whereDomainIs($qb, $url->domain());
$result = (int) $qb->getQuery()->getSingleScalarResult();
return $result > 0;
}
private function whereDomainIs(QueryBuilder $qb, ?string $domain): void
{
if ($domain !== null) {
$qb->join('s.domain', 'd')
->andWhere($qb->expr()->eq('d.authority', ':authority'))
->setParameter('authority', $domain);
} else {
$qb->andWhere($qb->expr()->isNull('s.domain'));
}
} }
} }

View file

@ -32,5 +32,5 @@ interface ShortUrlRepositoryInterface extends ObjectRepository
public function findOneMatching(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl; public function findOneMatching(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl;
public function importedUrlExists(ImportedShlinkUrl $url, bool $importShortCodes): bool; public function importedUrlExists(ImportedShlinkUrl $url): bool;
} }