<?php
declare(strict_types=1);
namespace BitBag\OpenMarketplace\Component\Stats\Event\Listener;
use BitBag\OpenMarketplace\Component\Channel\Entity\ChannelInterface;
use BitBag\OpenMarketplace\Component\Stats\Assigner\VendorToAnalyticsAssigner;
use BitBag\OpenMarketplace\Component\Stats\Checker\ExcludedRoutesCheckerInterface;
use BitBag\OpenMarketplace\Component\Stats\Entity\AnalyticsInterface;
use BitBag\OpenMarketplace\Component\Stats\Entity\StatisticsInterface;
use BitBag\OpenMarketplace\Component\Stats\Factory\AnalyticsFactoryInterface;
use BitBag\OpenMarketplace\Component\Stats\Factory\CrawlerDetect\CrawlerDetectFactoryInterface;
use BitBag\OpenMarketplace\Component\Stats\Factory\StatisticsFactoryInterface;
use BitBag\OpenMarketplace\Component\Stats\Handler\ImpressionsStatisticsAndAnalyticsHandler;
use BitBag\OpenMarketplace\Component\Stats\Repository\AnalyticsRepositoryInterface;
use BitBag\OpenMarketplace\Component\Stats\Resolver\UserCountryByUserIPResolver;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
final class StatisticsListener
{
private AnalyticsInterface $analytics;
private StatisticsInterface $statistics;
public function __construct(
private EntityManagerInterface $analyticsManager,
private AnalyticsRepositoryInterface $analyticsRepository,
private LoggerInterface $logger,
private ChannelContextInterface $channelContext,
private ImpressionsStatisticsAndAnalyticsHandler $impressionsStatisticsAndAnalyticsHandler,
private VendorToAnalyticsAssigner $vendorToAnalyticsAssigner,
private UserCountryByUserIPResolver $userCountryByUserIPResolver,
private ExcludedRoutesCheckerInterface $excludedRoutesChecker,
private AnalyticsFactoryInterface $analyticsFactory,
private StatisticsFactoryInterface $statisticsFactory,
private CrawlerDetectFactoryInterface $crawlerDetectFactory,
private string $excludedIps,
) {
}
public function onKernelTerminate(TerminateEvent $event): void
{
if ($this->isCrawler()) {
return;
}
$request = $event->getRequest();
$excludedIps = explode(',', $this->excludedIps);
if (in_array($request->getClientIp(), $excludedIps)) {
return;
}
if (!$event->isMainRequest()
|| $this->excludedRoutesChecker->shouldSkipRequest($request)) {
return;
}
$route = $request->attributes->get('_route');
$localeFromRequest = $request->getLocale();
$parsedUrl = parse_url($request->getUri());
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] . '://' : '';
$host = preg_replace('/^www\./', '', $parsedUrl['host'] ?? '');
$port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';
$path = $parsedUrl['path'] ?? '';
$mainUrl = $scheme . $host . $port . $path;
$url = mb_substr($mainUrl, 0, 254);
$this->statistics = $this->statisticsFactory->createStatistics(
(string) $request->getClientIp(),
$url,
(string) $request->headers->get('referer'),
$localeFromRequest
);
/** @var AnalyticsInterface|null $existingAnalytics */
$existingAnalytics = $this->analyticsRepository->findOneByUrlAndLocale($url, $localeFromRequest);
if (null === $existingAnalytics) {
$this->analytics = $this->analyticsFactory->createAnalytics(
$url,
$route,
$request->attributes->get('slug'),
$localeFromRequest
);
} else {
$this->analytics = $existingAnalytics;
}
$request = $event->getRequest();
if ('sylius_shop_product_show' === $route) {
$slug = $request->attributes->get('slug');
/** @var ChannelInterface $channel */
$channel = $this->channelContext->getChannel();
$localeCode = $localeFromRequest;
$this->vendorToAnalyticsAssigner->assignVendorToAnalyticsByProductSlug(
$this->analytics,
$channel,
$localeCode,
$slug
);
} elseif ('bitbag_sylius_elasticsearch_plugin_shop_list_products' === $route) {
$locale = $request->getLocale();
$clientIp = (string) $request->getClientIp();
$this->impressionsStatisticsAndAnalyticsHandler->handleImpressionsStatisticsAndAnalytics(
$locale,
$clientIp
);
}
$this->statistics->setUserCountry(
$this->userCountryByUserIPResolver->getUserCountryByIP(
(string) $request->getClientIp()
)
);
$this->persistAndFlushAnalyticsAndStatistics();
}
private function persistAndFlushAnalyticsAndStatistics(): void
{
try {
$this->statistics->setAnalytics($this->analytics);
$this->analytics->addStatistic($this->statistics);
$this->analyticsManager->persist($this->analytics);
$this->analyticsManager->flush();
} catch (\Throwable $exception) {
$this->logger->error($exception->getMessage());
}
}
private function isCrawler(): bool
{
return $this->crawlerDetectFactory->createNew()->isCrawler();
}
}