diff --git a/module/Core/src/Visit/Entity/ShortUrlVisitsCount.php b/module/Core/src/Visit/Entity/ShortUrlVisitsCount.php index ff3580b3..d513cfe8 100644 --- a/module/Core/src/Visit/Entity/ShortUrlVisitsCount.php +++ b/module/Core/src/Visit/Entity/ShortUrlVisitsCount.php @@ -12,8 +12,8 @@ class ShortUrlVisitsCount extends AbstractEntity public function __construct( private readonly ShortUrl $shortUrl, private readonly bool $potentialBot = false, - private readonly int $slotId = 1, - private readonly string $count = '1', + public readonly int $slotId = 1, + public readonly string $count = '1', ) { } } diff --git a/module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php b/module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php index 25df0b83..f62ddb3d 100644 --- a/module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php +++ b/module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php @@ -136,8 +136,9 @@ final class ShortUrlVisitsCountTracker $qb->forUpdate(); } - $resultSet = $qb->executeQuery()->fetchOne(); - $writeQb = ! $resultSet + $visitsCountId = $qb->executeQuery()->fetchOne(); + + $writeQb = ! $visitsCountId ? $conn->createQueryBuilder() ->insert('short_url_visits_counts') ->values([ @@ -145,18 +146,15 @@ final class ShortUrlVisitsCountTracker 'potential_bot' => ':potential_bot', 'slot_id' => ':slot_id', ]) - : $conn->createQueryBuilder() - ->update('short_url_visits_counts') - ->set('count', 'count + 1') - ->where($qb->expr()->and( - $qb->expr()->eq('short_url_id', ':short_url_id'), - $qb->expr()->eq('potential_bot', ':potential_bot'), - $qb->expr()->eq('slot_id', ':slot_id'), - )); - - $writeQb->setParameter('short_url_id', $shortUrlId) + ->setParameter('short_url_id', $shortUrlId) ->setParameter('potential_bot', $potentialBot ? '1' : '0') ->setParameter('slot_id', $slotId) - ->executeStatement(); + : $conn->createQueryBuilder() + ->update('short_url_visits_counts') + ->set('count', 'count + 1') + ->where($qb->expr()->eq('id', ':visits_count_id')) + ->setParameter('visits_count_id', $visitsCountId); + + $writeQb->executeStatement(); } } diff --git a/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php b/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php new file mode 100644 index 00000000..bfb5616f --- /dev/null +++ b/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php @@ -0,0 +1,76 @@ +repo = $this->getEntityManager()->getRepository(ShortUrlVisitsCount::class); + } + + #[Test] + public function createsNewEntriesWhenNoneExist(): void + { + $shortUrl = ShortUrl::createFake(); + $this->getEntityManager()->persist($shortUrl); + + $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $this->getEntityManager()->persist($visit); + $this->getEntityManager()->flush(); + + /** @var ShortUrlVisitsCount[] $result */ + $result = $this->repo->findBy(['shortUrl' => $shortUrl]); + + self::assertCount(1, $result); + self::assertEquals('1', $result[0]->count); + self::assertGreaterThanOrEqual(0, $result[0]->slotId); + self::assertLessThan(100, $result[0]->slotId); + } + + #[Test] + public function editsExistingEntriesWhenAlreadyExist(): void + { + $shortUrl = ShortUrl::createFake(); + $this->getEntityManager()->persist($shortUrl); + + for ($i = 0; $i < 100; $i++) { + $this->getEntityManager()->persist(new ShortUrlVisitsCount($shortUrl, slotId: $i)); + } + $this->getEntityManager()->flush(); + + $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $this->getEntityManager()->persist($visit); + $this->getEntityManager()->flush(); + + // Clear entity manager to force it to get fresh data from the database + // This is needed because the tracker inserts natively, bypassing the entity manager + $this->getEntityManager()->clear(); + + /** @var ShortUrlVisitsCount[] $result */ + $result = $this->repo->findBy(['shortUrl' => $shortUrl]); + $itemsWithCountBiggerThanOnce = array_values(array_filter( + $result, + static fn (ShortUrlVisitsCount $item) => ((int) $item->count) > 1, + )); + + self::assertCount(100, $result); + self::assertCount(1, $itemsWithCountBiggerThanOnce); + self::assertEquals('2', $itemsWithCountBiggerThanOnce[0]->count); + } +} diff --git a/phpunit-db.xml b/phpunit-db.xml index b883d8ca..3c5ffb64 100644 --- a/phpunit-db.xml +++ b/phpunit-db.xml @@ -20,6 +20,7 @@ ./module/*/src/Spec ./module/*/src/**/Spec ./module/*/src/**/**/Spec + ./module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9c85d2c4..4364c82c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -30,6 +30,7 @@ ./module/Core/src/Spec ./module/Core/src/**/Spec ./module/Core/src/**/**/Spec + ./module/Core/src/Visit/Listener/ShortUrlVisitsCountTracker.php