7 changed files with 182 additions and 69 deletions
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
import { Controller } from '@hotwired/stimulus'; |
||||
|
||||
/** |
||||
* Simple analytics controller to record page visits |
||||
*/ |
||||
export default class extends Controller { |
||||
static values = { |
||||
path: String |
||||
} |
||||
|
||||
connect() { |
||||
// Record the visit when the controller connects
|
||||
this.recordVisit(); |
||||
} |
||||
|
||||
recordVisit() { |
||||
// Get the current route path
|
||||
const path = this.pathValue || window.location.pathname; |
||||
|
||||
// Send visit data to API
|
||||
fetch('/api/visit', { |
||||
method: 'POST', |
||||
headers: { |
||||
'Content-Type': 'application/json', |
||||
}, |
||||
body: JSON.stringify({ |
||||
route: path |
||||
}) |
||||
}) |
||||
.catch(error => { |
||||
console.error('Error recording visit:', error); |
||||
}); |
||||
} |
||||
} |
||||
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace App\Controller\Api; |
||||
|
||||
use App\Entity\Visit; |
||||
use App\Repository\VisitRepository; |
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\HttpFoundation\JsonResponse; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Attribute\Route; |
||||
|
||||
class VisitController extends AbstractController |
||||
{ |
||||
#[Route('/api/visit', name: 'api_record_visit', methods: ['POST'])] |
||||
public function recordVisit(Request $request, VisitRepository $visitRepository): JsonResponse |
||||
{ |
||||
$data = json_decode($request->getContent(), true); |
||||
|
||||
if (!isset($data['route']) || empty($data['route'])) { |
||||
return new JsonResponse(['error' => 'Route is required'], Response::HTTP_BAD_REQUEST); |
||||
} |
||||
|
||||
$route = $data['route']; |
||||
$visit = new Visit($route); |
||||
|
||||
$visitRepository->save($visit); |
||||
|
||||
return new JsonResponse(['success' => true]); |
||||
} |
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace App\EventListener; |
||||
|
||||
use App\Entity\Visit; |
||||
use App\Repository\VisitRepository; |
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener; |
||||
use Symfony\Component\HttpKernel\Event\RequestEvent; |
||||
use Symfony\Component\HttpKernel\KernelEvents; |
||||
use Symfony\Bundle\SecurityBundle\Security; |
||||
|
||||
#[AsEventListener(event: KernelEvents::REQUEST, method: 'onKernelRequest', priority: 0)] |
||||
class VisitTrackingListener |
||||
{ |
||||
private const EXCLUDED_ROUTES = [ |
||||
'/api/', |
||||
'/_profiler', |
||||
'/_wdt', |
||||
'/service-worker.js', |
||||
'/robots.txt', |
||||
'/assets/', |
||||
'/icons/', |
||||
]; |
||||
|
||||
public function __construct( |
||||
private readonly VisitRepository $visitRepository, |
||||
private readonly Security $security, |
||||
) { |
||||
} |
||||
|
||||
public function onKernelRequest(RequestEvent $event): void |
||||
{ |
||||
// Only track main requests, not sub-requests |
||||
if (!$event->isMainRequest()) { |
||||
return; |
||||
} |
||||
|
||||
$request = $event->getRequest(); |
||||
$route = $request->getPathInfo(); |
||||
|
||||
// Skip tracking for excluded routes (API, profiler, assets, etc.) |
||||
foreach (self::EXCLUDED_ROUTES as $excludedRoute) { |
||||
if (str_starts_with($route, $excludedRoute)) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
// Get session ID if user is logged in |
||||
$sessionId = null; |
||||
if ($this->security->getUser()) { |
||||
// Start session if not already started |
||||
if (!$request->hasSession() || !$request->getSession()->isStarted()) { |
||||
$request->getSession()->start(); |
||||
} |
||||
$sessionId = $request->getSession()->getId(); |
||||
} |
||||
|
||||
// Create and save the visit record |
||||
$visit = new Visit($route, $sessionId); |
||||
|
||||
try { |
||||
$this->visitRepository->save($visit); |
||||
} catch (\Exception $e) { |
||||
// Silently fail to avoid breaking the request |
||||
// You could log this error if needed |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue