Files
shlink/module/Core/src/Visit/VisitsStatsHelper.php
2024-04-01 10:22:51 +02:00

153 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit;
use Doctrine\ORM\EntityManagerInterface;
use Pagerfanta\Adapter\AdapterInterface;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepository;
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
use Shlinkio\Shlink\Core\Tag\Repository\TagRepository;
use Shlinkio\Shlink\Core\Visit\Entity\OrphanVisitsCount;
use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount;
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\Paginator\Adapter\DomainVisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\NonOrphanVisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\OrphanVisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\ShortUrlVisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Paginator\Adapter\TagVisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\OrphanVisitsCountRepository;
use Shlinkio\Shlink\Core\Visit\Repository\ShortUrlVisitsCountRepository;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
{
public function __construct(private EntityManagerInterface $em)
{
}
public function getVisitsStats(?ApiKey $apiKey = null): VisitsStats
{
/** @var OrphanVisitsCountRepository $orphanVisitsCountRepo */
$orphanVisitsCountRepo = $this->em->getRepository(OrphanVisitsCount::class);
/** @var ShortUrlVisitsCountRepository $visitsCountRepo */
$visitsCountRepo = $this->em->getRepository(ShortUrlVisitsCount::class);
return new VisitsStats(
nonOrphanVisitsTotal: $visitsCountRepo->countNonOrphanVisits(new VisitsCountFiltering(apiKey: $apiKey)),
orphanVisitsTotal: $orphanVisitsCountRepo->countOrphanVisits(
new OrphanVisitsCountFiltering(apiKey: $apiKey),
),
nonOrphanVisitsNonBots: $visitsCountRepo->countNonOrphanVisits(
new VisitsCountFiltering(excludeBots: true, apiKey: $apiKey),
),
orphanVisitsNonBots: $orphanVisitsCountRepo->countOrphanVisits(
new OrphanVisitsCountFiltering(excludeBots: true, apiKey: $apiKey),
),
);
}
/**
* @return Visit[]|Paginator
* @throws ShortUrlNotFoundException
*/
public function visitsForShortUrl(
ShortUrlIdentifier $identifier,
VisitsParams $params,
?ApiKey $apiKey = null,
): Paginator {
/** @var ShortUrlRepositoryInterface $repo */
$repo = $this->em->getRepository(ShortUrl::class);
if (! $repo->shortCodeIsInUse($identifier, $apiKey?->spec())) {
throw ShortUrlNotFoundException::fromNotFound($identifier);
}
/** @var VisitRepositoryInterface $repo */
$repo = $this->em->getRepository(Visit::class);
return $this->createPaginator(
new ShortUrlVisitsPaginatorAdapter($repo, $identifier, $params, $apiKey),
$params,
);
}
/**
* @return Visit[]|Paginator
* @throws TagNotFoundException
*/
public function visitsForTag(string $tag, VisitsParams $params, ?ApiKey $apiKey = null): Paginator
{
/** @var TagRepository $tagRepo */
$tagRepo = $this->em->getRepository(Tag::class);
if (! $tagRepo->tagExists($tag, $apiKey)) {
throw TagNotFoundException::fromTag($tag);
}
/** @var VisitRepositoryInterface $repo */
$repo = $this->em->getRepository(Visit::class);
return $this->createPaginator(new TagVisitsPaginatorAdapter($repo, $tag, $params, $apiKey), $params);
}
/**
* @return Visit[]|Paginator
* @throws DomainNotFoundException
*/
public function visitsForDomain(string $domain, VisitsParams $params, ?ApiKey $apiKey = null): Paginator
{
/** @var DomainRepository $domainRepo */
$domainRepo = $this->em->getRepository(Domain::class);
if ($domain !== 'DEFAULT' && ! $domainRepo->domainExists($domain, $apiKey)) {
throw DomainNotFoundException::fromAuthority($domain);
}
/** @var VisitRepositoryInterface $repo */
$repo = $this->em->getRepository(Visit::class);
return $this->createPaginator(new DomainVisitsPaginatorAdapter($repo, $domain, $params, $apiKey), $params);
}
/**
* @return Visit[]|Paginator
*/
public function orphanVisits(OrphanVisitsParams $params, ?ApiKey $apiKey = null): Paginator
{
/** @var VisitRepositoryInterface $repo */
$repo = $this->em->getRepository(Visit::class);
return $this->createPaginator(new OrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params);
}
public function nonOrphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator
{
/** @var VisitRepositoryInterface $repo */
$repo = $this->em->getRepository(Visit::class);
return $this->createPaginator(new NonOrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params);
}
private function createPaginator(AdapterInterface $adapter, VisitsParams $params): Paginator
{
$paginator = new Paginator($adapter);
$paginator->setMaxPerPage($params->itemsPerPage)
->setCurrentPage($params->page);
return $paginator;
}
}