<?php
declare(strict_types=1);
namespace JanusHercules\ConversionEventTracking\Presentation\EventSubscriber;
use App\Entity\ConversionEvent;
use App\Service\ConversionEventService;
use App\Service\SessionService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use JanusHercules\ConversionEventTracking\Domain\Dto\CampaignSubcampaignPairDto;
use JanusHercules\ConversionEventTracking\Presentation\Service\Step0SessionServiceInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Event Subscriber für Step 1 Tracking (Link Clicked).
*
* Erkennt den __ggcts1 URL-Parameter und erstellt Step 1 Events
* für alle enthaltenen Campaign/Subcampaign-Paare.
*
* @see https://github.com/joboo-gmbh/joboo-marketing/blob/main/docs/outbound-url-tracking.md
*/
final class Step1TrackingSubscriber implements EventSubscriberInterface
{
/**
* URL-Parameter für Step 1 Tracking.
* __ggcts1 = go-gastro Conversion Tracking Step 1.
*/
private const PARAM_NAME = '__ggcts1';
public function __construct(
private readonly ConversionEventService $conversionEventService,
private readonly SessionService $sessionService,
private readonly Step0SessionServiceInterface $step0SessionService,
private readonly EntityManagerInterface $entityManager
) {
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}
/**
* @throws Exception
*/
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$paramValue = $request->query->get(self::PARAM_NAME);
if (empty($paramValue) || !is_string($paramValue)) {
return;
}
$session = $request->getSession();
if (!$session->isStarted()) {
return;
}
$step0EventIds = $this->step0SessionService->getAllStep0EventIds($session);
// Parse: "117:0,118:0" → [CampaignSubcampaignPairDto(117, 0), CampaignSubcampaignPairDto(118, 0)]
$pairs = CampaignSubcampaignPairDto::parseFromUrlParam($paramValue);
foreach ($pairs as $pair) {
$sessionKey = $pair->getSessionKey();
$step0EventId = $step0EventIds[$sessionKey] ?? null;
if ($step0EventId !== null) {
// Step 0 Event aus DB laden
/** @var ConversionEvent|null $step0Event */
$step0Event = $this->entityManager->find(ConversionEvent::class, $step0EventId);
if ($step0Event !== null) {
// Step 1 Event erstellen (referenziert auf Step 0)
$step1Event = $this->conversionEventService->createStepLinkClickedConversionEvent(
$step0Event,
$request
);
// Step 1 Event-ID für Goal-Tracking speichern
$this->sessionService->addConversionEventId(
$session,
$step1Event->getId(),
$pair->campaignId,
$pair->subcampaignId,
null
);
}
// Step 0 aus Session entfernen (wurde verarbeitet)
unset($step0EventIds[$sessionKey]);
} else {
// Fallback: Kein Step 0 vorhanden (z.B. JS war deaktiviert)
// → Step 0 + Step 1 zusammen erstellen
$this->conversionEventService->initiateGoalReachedConversionTracking(
null,
$pair->campaignId,
$pair->subcampaignId,
null,
$request
);
}
}
// Aktualisierte Step 0 IDs zurückschreiben:
// Die Session kann mehrere Step 0 Event-IDs enthalten (z.B. wenn der User mehrere
// Landingpages besucht hat). Oben wurden nur die Step 0 IDs verarbeitet, die im
// URL-Parameter __ggcts1 referenziert wurden – diese wurden via unset() aus dem
// Array entfernt. Hier schreiben wir das bereinigte Array zurück, damit:
// 1. Verarbeitete Step 0 IDs nicht erneut zu Step 1 konvertiert werden (F5/Zurück)
// 2. Nicht-verarbeitete Step 0 IDs für spätere Klicks erhalten bleiben
$this->step0SessionService->setAllStep0EventIds($session, $step0EventIds);
// Parameter aus Request entfernen (für saubere URLs in Logs etc.)
$request->query->remove(self::PARAM_NAME);
}
}