<?php
namespace App\EventSubscriber;
use App\Entity\Source;
use App\Entity\User;
use App\Entity\UserInterface;
use App\Event\ObjectEvent;
use App\Events;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Listens for user registration events and attaches a source entity to the user
* based on referral or UTM parameters found in the request query string.
*
* This subscriber stores the source identifier in the session during the initial
* request, then associates the matching Source entity to the User entity when the
* user is created.
*
* Supported query parameters:
* - ?source=SOURCE_ID
* - ?utm_source=UTM_IDENTIFIER
*
* Session key used: self::SESSION_NAME
*/
class SourceSubscriber implements EventSubscriberInterface
{
public const SESSION_NAME = 'app_source_data';
public const SOURCE_PARAMETER = 'source';
public const UTM_PARAMETER = 'utm_source';
/** @var RequestStack */
private $requestStack;
/** @var ManagerRegistry */
private $managerRegistry;
/**
* @param RequestStack $requestStack
* @param ManagerRegistry $managerRegistry
*/
public function __construct(RequestStack $requestStack, ManagerRegistry $managerRegistry)
{
$this->requestStack = $requestStack;
$this->managerRegistry = $managerRegistry;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
Events::USER_CREATED => 'onUserCreated',
];
}
/**
* @param RequestEvent $event
*/
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if ($request->isXmlHttpRequest()) {
return;
}
if (!$request->query->has(self::SOURCE_PARAMETER)
&& !$request->query->has(self::UTM_PARAMETER)
) {
return;
}
$request->getSession()->set(
self::SESSION_NAME,
$request->query->get(self::SOURCE_PARAMETER) ?: $request->query->get(self::UTM_PARAMETER)
);
}
/**
* @param ObjectEvent $event
*/
public function onUserCreated(ObjectEvent $event): void
{
try {
/** @var SessionInterface $session */
$session = $this->requestStack->getSession();
} catch (SessionNotFoundException $e) {
return;
}
if (!$session->has(self::SESSION_NAME)) {
return;
}
/** @var Source|null $source */
$source = null;
// Find via ID (ref)
try {
$source = $this->managerRegistry
->getRepository(Source::class)
->find($session->get(self::SESSION_NAME));
} catch (\Exception $e) {
}
// Find via UTM_SOURCE
if (!$source) {
$source = $this->managerRegistry
->getRepository(Source::class)
->findOneBy([
'utmSource' => $session->get(self::SESSION_NAME),
]);
}
if (!$source) {
return;
}
$entity = $event->getObject();
if ($entity instanceof UserInterface) {
$entity->setSource($source);
$this->managerRegistry
->getRepository(User::class)
->update($entity);
}
$session->remove(self::SESSION_NAME);
}
}