diff --git a/src/Controller/Administration/VisitorAnalyticsController.php b/src/Controller/Administration/VisitorAnalyticsController.php index cb2533c..d151020 100644 --- a/src/Controller/Administration/VisitorAnalyticsController.php +++ b/src/Controller/Administration/VisitorAnalyticsController.php @@ -63,6 +63,9 @@ class VisitorAnalyticsController extends AbstractController ]; } + // Article publish statistics + $articlePublishStats = $visitRepository->getArticlePublishStats(); + return $this->render('admin/analytics.html.twig', [ 'routeVisitCountsLast7Days' => $routeVisitCountsLast7Days, 'visitsLast24Hours' => $visitsLast24Hours, @@ -79,6 +82,7 @@ class VisitorAnalyticsController extends AbstractController 'recentVisitRecords' => $recentVisitRecords, 'dailyUniqueVisitorCountsLast7Days' => $dailyUniqueVisitorCountsLast7Days, 'topArticlesLast24Hours' => $topArticlesLast24Hours, + 'articlePublishStats' => $articlePublishStats, ]); } } diff --git a/src/EventListener/VisitTrackingListener.php b/src/EventListener/VisitTrackingListener.php index 6fbf77d..6611479 100644 --- a/src/EventListener/VisitTrackingListener.php +++ b/src/EventListener/VisitTrackingListener.php @@ -40,10 +40,15 @@ class VisitTrackingListener $request = $event->getRequest(); $route = $request->getPathInfo(); + // Exception: Always track article publish API calls + $isArticlePublish = str_starts_with($route, '/api/article/publish'); + // Skip tracking for excluded routes (API, profiler, assets, etc.) - foreach (self::EXCLUDED_ROUTES as $excludedRoute) { - if (str_starts_with($route, $excludedRoute)) { - return; + if (!$isArticlePublish) { + foreach (self::EXCLUDED_ROUTES as $excludedRoute) { + if (str_starts_with($route, $excludedRoute)) { + return; + } } } diff --git a/src/Repository/VisitRepository.php b/src/Repository/VisitRepository.php index 21c8201..0409a02 100644 --- a/src/Repository/VisitRepository.php +++ b/src/Repository/VisitRepository.php @@ -223,4 +223,40 @@ class VisitRepository extends ServiceEntityRepository ->setMaxResults($limit); return $qb->getQuery()->getResult(); } + + /** + * Returns the number of times the article publish API was called since a given datetime. + */ + public function countArticlePublishSince(\DateTimeImmutable $since): int + { + $qb = $this->createQueryBuilder('v') + ->select('COUNT(v.id)') + ->where('v.route = :route') + ->andWhere('v.visitedAt >= :since') + ->setParameter('route', '/api/article/publish') + ->setParameter('since', $since, \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE); + + return (int) $qb->getQuery()->getSingleScalarResult(); + } + + /** + * Returns article publish statistics for different time periods. + */ + public function getArticlePublishStats(): array + { + $qb = $this->createQueryBuilder('v') + ->select('COUNT(v.id)') + ->where('v.route = :route') + ->setParameter('route', '/api/article/publish'); + + $allTime = (int) $qb->getQuery()->getSingleScalarResult(); + + return [ + 'last_hour' => $this->countArticlePublishSince(new \DateTimeImmutable('-1 hour')), + 'last_24_hours' => $this->countArticlePublishSince(new \DateTimeImmutable('-24 hours')), + 'last_7_days' => $this->countArticlePublishSince(new \DateTimeImmutable('-7 days')), + 'last_30_days' => $this->countArticlePublishSince(new \DateTimeImmutable('-30 days')), + 'all_time' => $allTime, + ]; + } } diff --git a/templates/admin/analytics.html.twig b/templates/admin/analytics.html.twig index 723acd1..c92e33d 100644 --- a/templates/admin/analytics.html.twig +++ b/templates/admin/analytics.html.twig @@ -6,6 +6,20 @@

Page Visit Analytics

+
+
+

Article Publish Activity

+
    +
  • Last hour: {{ articlePublishStats.last_hour|number_format }}
  • +
  • Last 24 hours: {{ articlePublishStats.last_24_hours|number_format }}
  • +
  • Last 7 days: {{ articlePublishStats.last_7_days|number_format }}
  • +
  • Last 30 days: {{ articlePublishStats.last_30_days|number_format }}
  • +
  • All time: {{ articlePublishStats.all_time|number_format }}
  • +
+

Number of times articles were published via the API endpoint

+
+
+

Total Visits