diff --git a/module/CLI/src/Command/Tag/GetTagVisitsCommand.php b/module/CLI/src/Command/Tag/GetTagVisitsCommand.php index b3c083bc..2bd900e7 100644 --- a/module/CLI/src/Command/Tag/GetTagVisitsCommand.php +++ b/module/CLI/src/Command/Tag/GetTagVisitsCommand.php @@ -9,7 +9,7 @@ use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\Visit\Entity\Visit; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -39,7 +39,7 @@ class GetTagVisitsCommand extends AbstractVisitsListCommand protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator { $tag = $input->getArgument('tag'); - return $this->visitsHelper->visitsForTag($tag, new VisitsParams($dateRange)); + return $this->visitsHelper->visitsForTag($tag, new WithDomainVisitsParams($dateRange)); } /** diff --git a/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php b/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php index 445bd36f..692cc45f 100644 --- a/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php +++ b/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php @@ -8,7 +8,7 @@ use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\Visit\Entity\Visit; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Symfony\Component\Console\Input\InputInterface; @@ -35,7 +35,7 @@ class GetNonOrphanVisitsCommand extends AbstractVisitsListCommand */ protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator { - return $this->visitsHelper->nonOrphanVisits(new VisitsParams($dateRange)); + return $this->visitsHelper->nonOrphanVisits(new WithDomainVisitsParams($dateRange)); } /** diff --git a/module/Core/src/Model/AbstractInfinitePaginableListParams.php b/module/Core/src/Model/AbstractInfinitePaginableListParams.php index 70db853e..4d1f5f89 100644 --- a/module/Core/src/Model/AbstractInfinitePaginableListParams.php +++ b/module/Core/src/Model/AbstractInfinitePaginableListParams.php @@ -8,7 +8,7 @@ use Shlinkio\Shlink\Common\Paginator\Paginator; abstract class AbstractInfinitePaginableListParams { - private const FIRST_PAGE = 1; + private const int FIRST_PAGE = 1; public readonly int $page; public readonly int $itemsPerPage; diff --git a/module/Core/src/Visit/Model/OrphanVisitsParams.php b/module/Core/src/Visit/Model/OrphanVisitsParams.php index 6991928d..bff5b38c 100644 --- a/module/Core/src/Visit/Model/OrphanVisitsParams.php +++ b/module/Core/src/Visit/Model/OrphanVisitsParams.php @@ -9,20 +9,22 @@ use ValueError; use function Shlinkio\Shlink\Core\enumToString; use function sprintf; -final class OrphanVisitsParams extends VisitsParams +final class OrphanVisitsParams extends WithDomainVisitsParams { public function __construct( DateRange|null $dateRange = null, int|null $page = null, int|null $itemsPerPage = null, bool $excludeBots = false, + string|null $domain = null, public readonly OrphanVisitType|null $type = null, ) { - parent::__construct($dateRange, $page, $itemsPerPage, $excludeBots); + parent::__construct($dateRange, $page, $itemsPerPage, $excludeBots, $domain); } - public static function fromVisitsParamsAndRawData(VisitsParams $visitsParams, array $query): self + public static function fromRawData(array $query): self { + $visitsParams = WithDomainVisitsParams::fromRawData($query); $type = $query['type'] ?? null; return new self( @@ -30,6 +32,7 @@ final class OrphanVisitsParams extends VisitsParams page: $visitsParams->page, itemsPerPage: $visitsParams->itemsPerPage, excludeBots: $visitsParams->excludeBots, + domain: $visitsParams->domain, type: $type !== null ? self::parseType($type) : null, ); } diff --git a/module/Core/src/Visit/Model/WithDomainVisitsParams.php b/module/Core/src/Visit/Model/WithDomainVisitsParams.php new file mode 100644 index 00000000..edec3399 --- /dev/null +++ b/module/Core/src/Visit/Model/WithDomainVisitsParams.php @@ -0,0 +1,31 @@ +dateRange, + page: $visitsParams->page, + itemsPerPage: $visitsParams->itemsPerPage, + excludeBots: $visitsParams->excludeBots, + domain: $query['domain'] ?? null, + ); + } +} diff --git a/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php index 5e3cdbe1..e23cf10c 100644 --- a/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php +++ b/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php @@ -6,9 +6,9 @@ namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Visit\Entity\Visit; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -17,26 +17,28 @@ class NonOrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAda { public function __construct( private readonly VisitRepositoryInterface $repo, - private readonly VisitsParams $params, + private readonly WithDomainVisitsParams $params, private readonly ApiKey|null $apiKey, ) { } protected function doCount(): int { - return $this->repo->countNonOrphanVisits(new VisitsCountFiltering( + return $this->repo->countNonOrphanVisits(new WithDomainVisitsCountFiltering( $this->params->dateRange, $this->params->excludeBots, $this->apiKey, + $this->params->domain, )); } public function getSlice(int $offset, int $length): iterable { - return $this->repo->findNonOrphanVisits(new VisitsListFiltering( + return $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering( $this->params->dateRange, $this->params->excludeBots, $this->apiKey, + $this->params->domain, $length, $offset, )); diff --git a/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php index 899ab831..fbb14c42 100644 --- a/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php +++ b/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php @@ -28,6 +28,7 @@ class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte dateRange: $this->params->dateRange, excludeBots: $this->params->excludeBots, apiKey: $this->apiKey, + domain: $this->params->domain, type: $this->params->type, )); } @@ -38,6 +39,7 @@ class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte dateRange: $this->params->dateRange, excludeBots: $this->params->excludeBots, apiKey: $this->apiKey, + domain: $this->params->domain, type: $this->params->type, limit: $length, offset: $offset, diff --git a/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php index 909bd2ba..a76b7524 100644 --- a/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php +++ b/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php @@ -6,9 +6,9 @@ namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Visit\Entity\Visit; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -18,7 +18,7 @@ class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter public function __construct( private readonly VisitRepositoryInterface $visitRepository, private readonly string $tag, - private readonly VisitsParams $params, + private readonly WithDomainVisitsParams $params, private readonly ApiKey|null $apiKey, ) { } @@ -27,10 +27,11 @@ class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter { return $this->visitRepository->findVisitsByTag( $this->tag, - new VisitsListFiltering( + new WithDomainVisitsListFiltering( $this->params->dateRange, $this->params->excludeBots, $this->apiKey, + $this->params->domain, $length, $offset, ), @@ -41,10 +42,11 @@ class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter { return $this->visitRepository->countVisitsByTag( $this->tag, - new VisitsCountFiltering( + new WithDomainVisitsCountFiltering( $this->params->dateRange, $this->params->excludeBots, $this->apiKey, + $this->params->domain, ), ); } diff --git a/module/Core/src/Visit/Persistence/OrphanVisitsCountFiltering.php b/module/Core/src/Visit/Persistence/OrphanVisitsCountFiltering.php index c09bc5ca..981c26d4 100644 --- a/module/Core/src/Visit/Persistence/OrphanVisitsCountFiltering.php +++ b/module/Core/src/Visit/Persistence/OrphanVisitsCountFiltering.php @@ -8,14 +8,15 @@ use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitType; use Shlinkio\Shlink\Rest\Entity\ApiKey; -class OrphanVisitsCountFiltering extends VisitsCountFiltering +class OrphanVisitsCountFiltering extends WithDomainVisitsCountFiltering { public function __construct( DateRange|null $dateRange = null, bool $excludeBots = false, ApiKey|null $apiKey = null, + string|null $domain = null, public readonly OrphanVisitType|null $type = null, ) { - parent::__construct($dateRange, $excludeBots, $apiKey); + parent::__construct($dateRange, $excludeBots, $apiKey, $domain); } } diff --git a/module/Core/src/Visit/Persistence/OrphanVisitsListFiltering.php b/module/Core/src/Visit/Persistence/OrphanVisitsListFiltering.php index d1e49605..a60a0690 100644 --- a/module/Core/src/Visit/Persistence/OrphanVisitsListFiltering.php +++ b/module/Core/src/Visit/Persistence/OrphanVisitsListFiltering.php @@ -14,10 +14,11 @@ final class OrphanVisitsListFiltering extends OrphanVisitsCountFiltering DateRange|null $dateRange = null, bool $excludeBots = false, ApiKey|null $apiKey = null, + string|null $domain = null, OrphanVisitType|null $type = null, public readonly int|null $limit = null, public readonly int|null $offset = null, ) { - parent::__construct($dateRange, $excludeBots, $apiKey, $type); + parent::__construct($dateRange, $excludeBots, $apiKey, $domain, $type); } } diff --git a/module/Core/src/Visit/Persistence/WithDomainVisitsCountFiltering.php b/module/Core/src/Visit/Persistence/WithDomainVisitsCountFiltering.php new file mode 100644 index 00000000..5d5cc74a --- /dev/null +++ b/module/Core/src/Visit/Persistence/WithDomainVisitsCountFiltering.php @@ -0,0 +1,20 @@ +createVisitsByTagQueryBuilder($tag, $filtering); return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset); @@ -173,7 +174,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo /** * @return Visit[] */ - public function findNonOrphanVisits(VisitsListFiltering $filtering): array + public function findNonOrphanVisits(WithDomainVisitsListFiltering $filtering): array { $qb = $this->createAllVisitsQueryBuilder($filtering); $qb->andWhere($qb->expr()->isNotNull('v.shortUrl')); @@ -193,8 +194,9 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo return (int) $this->matchSingleScalarResult(new CountOfNonOrphanVisits($filtering)); } - private function createAllVisitsQueryBuilder(VisitsListFiltering|OrphanVisitsListFiltering $filtering): QueryBuilder - { + private function createAllVisitsQueryBuilder( + VisitsListFiltering|OrphanVisitsListFiltering|WithDomainVisitsListFiltering $filtering, + ): QueryBuilder { // Parameters in this query need to be inlined, not bound, as we need to use it as sub-query later // Since they are not provided by the caller, it's reasonably safe $qb = $this->getEntityManager()->createQueryBuilder(); diff --git a/module/Core/src/Visit/Repository/VisitRepositoryInterface.php b/module/Core/src/Visit/Repository/VisitRepositoryInterface.php index b5590f8c..ca97d12e 100644 --- a/module/Core/src/Visit/Repository/VisitRepositoryInterface.php +++ b/module/Core/src/Visit/Repository/VisitRepositoryInterface.php @@ -12,6 +12,8 @@ use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; /** * @extends ObjectRepository @@ -28,9 +30,9 @@ interface VisitRepositoryInterface extends ObjectRepository, EntitySpecification /** * @return Visit[] */ - public function findVisitsByTag(string $tag, VisitsListFiltering $filtering): array; + public function findVisitsByTag(string $tag, WithDomainVisitsListFiltering $filtering): array; - public function countVisitsByTag(string $tag, VisitsCountFiltering $filtering): int; + public function countVisitsByTag(string $tag, WithDomainVisitsCountFiltering $filtering): int; /** * @return Visit[] @@ -49,9 +51,9 @@ interface VisitRepositoryInterface extends ObjectRepository, EntitySpecification /** * @return Visit[] */ - public function findNonOrphanVisits(VisitsListFiltering $filtering): array; + public function findNonOrphanVisits(WithDomainVisitsListFiltering $filtering): array; - public function countNonOrphanVisits(VisitsCountFiltering $filtering): int; + public function countNonOrphanVisits(WithDomainVisitsCountFiltering $filtering): int; public function findMostRecentOrphanVisit(): Visit|null; } diff --git a/module/Core/src/Visit/VisitsStatsHelper.php b/module/Core/src/Visit/VisitsStatsHelper.php index 412decc7..65e710c7 100644 --- a/module/Core/src/Visit/VisitsStatsHelper.php +++ b/module/Core/src/Visit/VisitsStatsHelper.php @@ -23,6 +23,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\DomainVisitsPaginatorAdapter; use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\NonOrphanVisitsPaginatorAdapter; use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\OrphanVisitsPaginatorAdapter; @@ -88,7 +89,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface /** * @inheritDoc */ - public function visitsForTag(string $tag, VisitsParams $params, ApiKey|null $apiKey = null): Paginator + public function visitsForTag(string $tag, WithDomainVisitsParams $params, ApiKey|null $apiKey = null): Paginator { /** @var TagRepository $tagRepo */ $tagRepo = $this->em->getRepository(Tag::class); @@ -130,7 +131,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface return $this->createPaginator(new OrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params); } - public function nonOrphanVisits(VisitsParams $params, ApiKey|null $apiKey = null): Paginator + public function nonOrphanVisits(WithDomainVisitsParams $params, ApiKey|null $apiKey = null): Paginator { /** @var VisitRepository $repo */ $repo = $this->em->getRepository(Visit::class); diff --git a/module/Core/src/Visit/VisitsStatsHelperInterface.php b/module/Core/src/Visit/VisitsStatsHelperInterface.php index 12e58933..418eae61 100644 --- a/module/Core/src/Visit/VisitsStatsHelperInterface.php +++ b/module/Core/src/Visit/VisitsStatsHelperInterface.php @@ -13,6 +13,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Rest\Entity\ApiKey; interface VisitsStatsHelperInterface @@ -33,7 +34,7 @@ interface VisitsStatsHelperInterface * @return Paginator * @throws TagNotFoundException */ - public function visitsForTag(string $tag, VisitsParams $params, ApiKey|null $apiKey = null): Paginator; + public function visitsForTag(string $tag, WithDomainVisitsParams $params, ApiKey|null $apiKey = null): Paginator; /** * @return Paginator @@ -49,5 +50,5 @@ interface VisitsStatsHelperInterface /** * @return Paginator */ - public function nonOrphanVisits(VisitsParams $params, ApiKey|null $apiKey = null): Paginator; + public function nonOrphanVisits(WithDomainVisitsParams $params, ApiKey|null $apiKey = null): Paginator; } diff --git a/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php b/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php index fe53629f..755b9bf8 100644 --- a/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php +++ b/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php @@ -22,6 +22,7 @@ use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\OrphanVisitsCountRepository; use Shlinkio\Shlink\Core\Visit\Repository\ShortUrlVisitsCountRepository; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepository; @@ -187,13 +188,13 @@ class VisitRepositoryTest extends DatabaseTestCase $this->createShortUrlsAndVisits(false, [$foo]); $this->getEntityManager()->flush(); - self::assertCount(0, $this->repo->findVisitsByTag('invalid', new VisitsListFiltering())); - self::assertCount(18, $this->repo->findVisitsByTag($foo, new VisitsListFiltering())); - self::assertCount(12, $this->repo->findVisitsByTag($foo, new VisitsListFiltering(null, true))); - self::assertCount(6, $this->repo->findVisitsByTag($foo, new VisitsListFiltering( + self::assertCount(0, $this->repo->findVisitsByTag('invalid', new WithDomainVisitsListFiltering())); + self::assertCount(18, $this->repo->findVisitsByTag($foo, new WithDomainVisitsListFiltering())); + self::assertCount(12, $this->repo->findVisitsByTag($foo, new WithDomainVisitsListFiltering(null, true))); + self::assertCount(6, $this->repo->findVisitsByTag($foo, new WithDomainVisitsListFiltering( DateRange::between(Chronos::parse('2016-01-02'), Chronos::parse('2016-01-03')), ))); - self::assertCount(12, $this->repo->findVisitsByTag($foo, new VisitsListFiltering( + self::assertCount(12, $this->repo->findVisitsByTag($foo, new WithDomainVisitsListFiltering( DateRange::since(Chronos::parse('2016-01-03')), ))); } @@ -479,31 +480,38 @@ class VisitRepositoryTest extends DatabaseTestCase $this->getEntityManager()->flush(); - self::assertCount(21, $this->repo->findNonOrphanVisits(new VisitsListFiltering())); - self::assertCount(21, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::allTime()))); - self::assertCount(4, $this->repo->findNonOrphanVisits(new VisitsListFiltering(apiKey: $authoredApiKey))); - self::assertCount(7, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::since( + self::assertCount(21, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering())); + self::assertCount(21, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering( + DateRange::allTime(), + ))); + self::assertCount(4, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering( + apiKey: $authoredApiKey, + ))); + self::assertCount(7, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(DateRange::since( Chronos::parse('2016-01-05')->endOfDay(), )))); - self::assertCount(12, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::until( + self::assertCount(12, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(DateRange::until( Chronos::parse('2016-01-04')->endOfDay(), )))); - self::assertCount(6, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::between( + self::assertCount(6, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(DateRange::between( Chronos::parse('2016-01-03')->startOfDay(), Chronos::parse('2016-01-04')->endOfDay(), )))); - self::assertCount(13, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::between( + self::assertCount(13, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(DateRange::between( Chronos::parse('2016-01-03')->startOfDay(), Chronos::parse('2016-01-08')->endOfDay(), )))); - self::assertCount(3, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::between( + self::assertCount(3, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(DateRange::between( Chronos::parse('2016-01-03')->startOfDay(), Chronos::parse('2016-01-08')->endOfDay(), ), limit: 10, offset: 10))); - self::assertCount(15, $this->repo->findNonOrphanVisits(new VisitsListFiltering(excludeBots: true))); - self::assertCount(10, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 10))); - self::assertCount(1, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 10, offset: 20))); - self::assertCount(5, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 5, offset: 5))); + self::assertCount(15, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(excludeBots: true))); + self::assertCount(10, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(limit: 10))); + self::assertCount(1, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering( + limit: 10, + offset: 20, + ))); + self::assertCount(5, $this->repo->findNonOrphanVisits(new WithDomainVisitsListFiltering(limit: 5, offset: 5))); } #[Test] diff --git a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php index 2dbaa25a..d021646d 100644 --- a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php +++ b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php @@ -10,10 +10,10 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\NonOrphanVisitsPaginatorAdapter; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -21,13 +21,13 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase { private NonOrphanVisitsPaginatorAdapter $adapter; private MockObject & VisitRepositoryInterface $repo; - private VisitsParams $params; + private WithDomainVisitsParams $params; private ApiKey $apiKey; protected function setUp(): void { $this->repo = $this->createMock(VisitRepositoryInterface::class); - $this->params = VisitsParams::fromRawData([]); + $this->params = WithDomainVisitsParams::fromRawData([]); $this->apiKey = ApiKey::create(); $this->adapter = new NonOrphanVisitsPaginatorAdapter($this->repo, $this->params, $this->apiKey); @@ -38,7 +38,7 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase { $expectedCount = 5; $this->repo->expects($this->once())->method('countNonOrphanVisits')->with( - new VisitsCountFiltering($this->params->dateRange, $this->params->excludeBots, $this->apiKey), + new WithDomainVisitsCountFiltering($this->params->dateRange, $this->params->excludeBots, $this->apiKey), )->willReturn($expectedCount); $result = $this->adapter->getNbResults(); @@ -55,12 +55,12 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase { $visitor = Visitor::empty(); $list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)]; - $this->repo->expects($this->once())->method('findNonOrphanVisits')->with(new VisitsListFiltering( + $this->repo->expects($this->once())->method('findNonOrphanVisits')->with(new WithDomainVisitsListFiltering( $this->params->dateRange, $this->params->excludeBots, $this->apiKey, - $limit, - $offset, + limit: $limit, + offset: $offset, ))->willReturn($list); $result = $this->adapter->getSlice($offset, $limit); diff --git a/module/Core/test/Visit/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php index c0cd4d0b..cbecd958 100644 --- a/module/Core/test/Visit/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php +++ b/module/Core/test/Visit/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php @@ -8,10 +8,10 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Util\DateRange; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\TagVisitsPaginatorAdapter; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -33,7 +33,7 @@ class VisitsForTagPaginatorAdapterTest extends TestCase $adapter = $this->createAdapter(null); $this->repo->expects($this->exactly($count))->method('findVisitsByTag')->with( 'foo', - new VisitsListFiltering(DateRange::allTime(), false, null, $limit, $offset), + new WithDomainVisitsListFiltering(DateRange::allTime(), limit: $limit, offset: $offset), )->willReturn([]); for ($i = 0; $i < $count; $i++) { @@ -49,7 +49,7 @@ class VisitsForTagPaginatorAdapterTest extends TestCase $adapter = $this->createAdapter($apiKey); $this->repo->expects($this->once())->method('countVisitsByTag')->with( 'foo', - new VisitsCountFiltering(DateRange::allTime(), false, $apiKey), + new WithDomainVisitsCountFiltering(DateRange::allTime(), apiKey: $apiKey), )->willReturn(3); for ($i = 0; $i < $count; $i++) { @@ -59,6 +59,6 @@ class VisitsForTagPaginatorAdapterTest extends TestCase private function createAdapter(ApiKey|null $apiKey): TagVisitsPaginatorAdapter { - return new TagVisitsPaginatorAdapter($this->repo, 'foo', VisitsParams::fromRawData([]), $apiKey); + return new TagVisitsPaginatorAdapter($this->repo, 'foo', WithDomainVisitsParams::fromRawData([]), $apiKey); } } diff --git a/module/Core/test/Visit/VisitsStatsHelperTest.php b/module/Core/test/Visit/VisitsStatsHelperTest.php index 070ea7e6..8d75f523 100644 --- a/module/Core/test/Visit/VisitsStatsHelperTest.php +++ b/module/Core/test/Visit/VisitsStatsHelperTest.php @@ -29,10 +29,12 @@ use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; -use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsCountFiltering; +use Shlinkio\Shlink\Core\Visit\Persistence\WithDomainVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Repository\OrphanVisitsCountRepository; use Shlinkio\Shlink\Core\Visit\Repository\ShortUrlVisitsCountRepository; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepository; @@ -147,7 +149,7 @@ class VisitsStatsHelperTest extends TestCase $this->expectException(TagNotFoundException::class); - $this->helper->visitsForTag($tag, new VisitsParams(), $apiKey); + $this->helper->visitsForTag($tag, new WithDomainVisitsParams(), $apiKey); } #[Test, DataProviderExternal(ApiKeyDataProviders::class, 'adminApiKeysProvider')] @@ -170,7 +172,7 @@ class VisitsStatsHelperTest extends TestCase [Visit::class, $repo2], ]); - $paginator = $this->helper->visitsForTag($tag, new VisitsParams(), $apiKey); + $paginator = $this->helper->visitsForTag($tag, new WithDomainVisitsParams(), $apiKey); self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults())); } @@ -265,14 +267,14 @@ class VisitsStatsHelperTest extends TestCase ); $repo = $this->createMock(VisitRepository::class); $repo->expects($this->once())->method('countNonOrphanVisits')->with( - $this->isInstanceOf(VisitsCountFiltering::class), + $this->isInstanceOf(WithDOmainVisitsCountFiltering::class), )->willReturn(count($list)); $repo->expects($this->once())->method('findNonOrphanVisits')->with( - $this->isInstanceOf(VisitsListFiltering::class), + $this->isInstanceOf(WithDOmainVisitsListFiltering::class), )->willReturn($list); $this->em->expects($this->once())->method('getRepository')->with(Visit::class)->willReturn($repo); - $paginator = $this->helper->nonOrphanVisits(new VisitsParams()); + $paginator = $this->helper->nonOrphanVisits(new WithDomainVisitsParams()); self::assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentPageResults())); } diff --git a/module/Rest/src/Action/Visit/AbstractListVisitsAction.php b/module/Rest/src/Action/Visit/AbstractListVisitsAction.php index b63133fa..6e49c46f 100644 --- a/module/Rest/src/Action/Visit/AbstractListVisitsAction.php +++ b/module/Rest/src/Action/Visit/AbstractListVisitsAction.php @@ -10,7 +10,6 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils; use Shlinkio\Shlink\Core\Visit\Entity\Visit; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -26,9 +25,8 @@ abstract class AbstractListVisitsAction extends AbstractRestAction public function handle(ServerRequestInterface $request): ResponseInterface { - $params = VisitsParams::fromRawData($request->getQueryParams()); $apiKey = AuthenticationMiddleware::apiKeyFromRequest($request); - $visits = $this->getVisitsPaginator($request, $params, $apiKey); + $visits = $this->getVisitsPaginator($request, $apiKey); return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]); } @@ -36,9 +34,5 @@ abstract class AbstractListVisitsAction extends AbstractRestAction /** * @return Pagerfanta */ - abstract protected function getVisitsPaginator( - ServerRequestInterface $request, - VisitsParams $params, - ApiKey $apiKey, - ): Pagerfanta; + abstract protected function getVisitsPaginator(ServerRequestInterface $request, ApiKey $apiKey): Pagerfanta; } diff --git a/module/Rest/src/Action/Visit/DomainVisitsAction.php b/module/Rest/src/Action/Visit/DomainVisitsAction.php index 0e027955..dd1ad292 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -23,8 +23,9 @@ class DomainVisitsAction extends AbstractListVisitsAction parent::__construct($visitsHelper); } - protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta + protected function getVisitsPaginator(Request $request, ApiKey $apiKey): Pagerfanta { + $params = VisitsParams::fromRawData($request->getQueryParams()); $domain = $this->resolveDomainParam($request); return $this->visitsHelper->visitsForDomain($domain, $params, $apiKey); } diff --git a/module/Rest/src/Action/Visit/NonOrphanVisitsAction.php b/module/Rest/src/Action/Visit/NonOrphanVisitsAction.php index b2f7471b..3bcc9929 100644 --- a/module/Rest/src/Action/Visit/NonOrphanVisitsAction.php +++ b/module/Rest/src/Action/Visit/NonOrphanVisitsAction.php @@ -6,18 +6,16 @@ namespace Shlinkio\Shlink\Rest\Action\Visit; use Pagerfanta\Pagerfanta; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Rest\Entity\ApiKey; class NonOrphanVisitsAction extends AbstractListVisitsAction { protected const string ROUTE_PATH = '/visits/non-orphan'; - protected function getVisitsPaginator( - ServerRequestInterface $request, - VisitsParams $params, - ApiKey $apiKey, - ): Pagerfanta { + protected function getVisitsPaginator(ServerRequestInterface $request, ApiKey $apiKey): Pagerfanta + { + $params = WithDomainVisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->nonOrphanVisits($params, $apiKey); } } diff --git a/module/Rest/src/Action/Visit/OrphanVisitsAction.php b/module/Rest/src/Action/Visit/OrphanVisitsAction.php index b3c246ca..4e1f7745 100644 --- a/module/Rest/src/Action/Visit/OrphanVisitsAction.php +++ b/module/Rest/src/Action/Visit/OrphanVisitsAction.php @@ -7,19 +7,15 @@ namespace Shlinkio\Shlink\Rest\Action\Visit; use Pagerfanta\Pagerfanta; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Rest\Entity\ApiKey; class OrphanVisitsAction extends AbstractListVisitsAction { protected const string ROUTE_PATH = '/visits/orphan'; - protected function getVisitsPaginator( - ServerRequestInterface $request, - VisitsParams $params, - ApiKey $apiKey, - ): Pagerfanta { - $orphanParams = OrphanVisitsParams::fromVisitsParamsAndRawData($params, $request->getQueryParams()); + protected function getVisitsPaginator(ServerRequestInterface $request, ApiKey $apiKey): Pagerfanta + { + $orphanParams = OrphanVisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->orphanVisits($orphanParams, $apiKey); } } diff --git a/module/Rest/src/Action/Visit/ShortUrlVisitsAction.php b/module/Rest/src/Action/Visit/ShortUrlVisitsAction.php index d8fc36e9..1d720d35 100644 --- a/module/Rest/src/Action/Visit/ShortUrlVisitsAction.php +++ b/module/Rest/src/Action/Visit/ShortUrlVisitsAction.php @@ -14,8 +14,9 @@ class ShortUrlVisitsAction extends AbstractListVisitsAction { protected const string ROUTE_PATH = '/short-urls/{shortCode}/visits'; - protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta + protected function getVisitsPaginator(Request $request, ApiKey $apiKey): Pagerfanta { + $params = VisitsParams::fromRawData($request->getQueryParams()); $identifier = ShortUrlIdentifier::fromApiRequest($request); return $this->visitsHelper->visitsForShortUrl($identifier, $params, $apiKey); } diff --git a/module/Rest/src/Action/Visit/TagVisitsAction.php b/module/Rest/src/Action/Visit/TagVisitsAction.php index 07ad7167..bf43edaa 100644 --- a/module/Rest/src/Action/Visit/TagVisitsAction.php +++ b/module/Rest/src/Action/Visit/TagVisitsAction.php @@ -6,15 +6,16 @@ namespace Shlinkio\Shlink\Rest\Action\Visit; use Pagerfanta\Pagerfanta; use Psr\Http\Message\ServerRequestInterface as Request; -use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\WithDomainVisitsParams; use Shlinkio\Shlink\Rest\Entity\ApiKey; class TagVisitsAction extends AbstractListVisitsAction { protected const string ROUTE_PATH = '/tags/{tag}/visits'; - protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta + protected function getVisitsPaginator(Request $request, ApiKey $apiKey): Pagerfanta { + $params = WithDomainVisitsParams::fromRawData($request->getQueryParams()); $tag = $request->getAttribute('tag', ''); return $this->visitsHelper->visitsForTag($tag, $params, $apiKey); }