src/Service/ViewTrackingService.php line 160

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\Product;
  4. use App\Entity\Shop;
  5. use App\Entity\User;
  6. use App\Entity\UserProductView;
  7. use App\Repository\ProductRepository;
  8. use App\Repository\ShopRepository;
  9. use App\Repository\UserProductViewRepository;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use Symfony\Component\HttpFoundation\RequestStack;
  12. class ViewTrackingService
  13. {
  14.     private EntityManagerInterface $entityManager;
  15.     private RequestStack $requestStack;
  16.     private ProductRepository $productRepository;
  17.     private ShopRepository $shopRepository;
  18.     private UserProductViewRepository $userProductViewRepository;
  19.     public function __construct(
  20.         EntityManagerInterface $entityManager,
  21.         RequestStack $requestStack,
  22.         ProductRepository $productRepository,
  23.         ShopRepository $shopRepository,
  24.         UserProductViewRepository $userProductViewRepository
  25.     ) {
  26.         $this->entityManager $entityManager;
  27.         $this->requestStack $requestStack;
  28.         $this->productRepository $productRepository;
  29.         $this->shopRepository $shopRepository;
  30.         $this->userProductViewRepository $userProductViewRepository;
  31.     }
  32.     /**
  33.      * Incrémente le compteur de vues d'un produit
  34.      */
  35.     public function trackProductView(Product $product, ?User $user null): void
  36.     {
  37.         // Vérifier si cette vue a déjà été comptée pour cette session
  38.         if ($this->hasViewedProduct($product)) {
  39.             return;
  40.         }
  41.         // Incrémenter le compteur de vues
  42.         $product->setViewCount(($product->getViewCount() ?? 0) + 1);
  43.         
  44.         // Incrémenter aussi les vues de la boutique
  45.         if ($product->getShop()) {
  46.             $product->getShop()->incrementViewCount();
  47.         }
  48.         // Enregistrer l'historique pour les utilisateurs connectés
  49.         if ($user) {
  50.             $this->recordUserProductView($user$product);
  51.         }
  52.         $this->entityManager->flush();
  53.         // Marquer comme vue dans la session
  54.         $this->markProductAsViewed($product);
  55.     }
  56.     /**
  57.      * Enregistre une vue de produit pour un utilisateur
  58.      */
  59.     private function recordUserProductView(User $userProduct $product): void
  60.     {
  61.         // Vérifier si l'utilisateur a déjà vu ce produit récemment (dans les 5 dernières minutes)
  62.         // pour éviter les doublons
  63.         $recentView $this->userProductViewRepository->createQueryBuilder('upv')
  64.             ->where('upv.user = :user')
  65.             ->andWhere('upv.product = :product')
  66.             ->andWhere('upv.viewedAt > :recentTime')
  67.             ->setParameter('user'$user)
  68.             ->setParameter('product'$product)
  69.             ->setParameter('recentTime', new \DateTimeImmutable('-5 minutes'))
  70.             ->setMaxResults(1)
  71.             ->getQuery()
  72.             ->getOneOrNullResult();
  73.         if ($recentView) {
  74.             return; // Vue déjà enregistrée récemment
  75.         }
  76.         // Créer une nouvelle entrée
  77.         $userProductView = new UserProductView();
  78.         $userProductView->setUser($user);
  79.         $userProductView->setProduct($product);
  80.         $userProductView->setViewedAt(new \DateTimeImmutable());
  81.         $this->entityManager->persist($userProductView);
  82.     }
  83.     /**
  84.      * Incrémente le compteur de vues d'une boutique
  85.      */
  86.     public function trackShopView(Shop $shop): void
  87.     {
  88.         // Vérifier si cette vue a déjà été comptée pour cette session
  89.         if ($this->hasViewedShop($shop)) {
  90.             return;
  91.         }
  92.         // Incrémenter le compteur de vues
  93.         $shop->incrementViewCount();
  94.         $this->entityManager->flush();
  95.         // Marquer comme vue dans la session
  96.         $this->markShopAsViewed($shop);
  97.     }
  98.     /**
  99.      * Vérifie si un produit a déjà été vu dans cette session
  100.      */
  101.     private function hasViewedProduct(Product $product): bool
  102.     {
  103.         $session $this->requestStack->getSession();
  104.         $viewedProducts $session->get('viewed_products', []);
  105.         
  106.         return in_array($product->getId(), $viewedProducts);
  107.     }
  108.     /**
  109.      * Vérifie si une boutique a déjà été vue dans cette session
  110.      */
  111.     private function hasViewedShop(Shop $shop): bool
  112.     {
  113.         $session $this->requestStack->getSession();
  114.         $viewedShops $session->get('viewed_shops', []);
  115.         
  116.         return in_array($shop->getId(), $viewedShops);
  117.     }
  118.     /**
  119.      * Marque un produit comme vu dans la session
  120.      */
  121.     private function markProductAsViewed(Product $product): void
  122.     {
  123.         $session $this->requestStack->getSession();
  124.         $viewedProducts $session->get('viewed_products', []);
  125.         $viewedProducts[] = $product->getId();
  126.         
  127.         // Limiter à 100 produits vus pour éviter une session trop lourde
  128.         if (count($viewedProducts) > 100) {
  129.             $viewedProducts array_slice($viewedProducts, -100);
  130.         }
  131.         
  132.         $session->set('viewed_products'$viewedProducts);
  133.     }
  134.     /**
  135.      * Marque une boutique comme vue dans la session
  136.      */
  137.     private function markShopAsViewed(Shop $shop): void
  138.     {
  139.         $session $this->requestStack->getSession();
  140.         $viewedShops $session->get('viewed_shops', []);
  141.         $viewedShops[] = $shop->getId();
  142.         
  143.         // Limiter à 50 boutiques vues pour éviter une session trop lourde
  144.         if (count($viewedShops) > 50) {
  145.             $viewedShops array_slice($viewedShops, -50);
  146.         }
  147.         
  148.         $session->set('viewed_shops'$viewedShops);
  149.     }
  150.     /**
  151.      * Récupère les produits récemment vus
  152.      */
  153.     public function getRecentlyViewedProducts(int $limit 10): array
  154.     {
  155.         $session $this->requestStack->getSession();
  156.         $viewedProductIds $session->get('viewed_products', []);
  157.         
  158.         if (empty($viewedProductIds)) {
  159.             return [];
  160.         }
  161.         // Récupérer les derniers produits vus (ordre inverse)
  162.         $viewedProductIds array_reverse($viewedProductIds);
  163.         $viewedProductIds array_slice($viewedProductIds0$limit);
  164.         return $this->productRepository->findBy(['id' => $viewedProductIds]);
  165.     }
  166.     /**
  167.      * Récupère les boutiques récemment vues
  168.      */
  169.     public function getRecentlyViewedShops(int $limit 5): array
  170.     {
  171.         $session $this->requestStack->getSession();
  172.         $viewedShopIds $session->get('viewed_shops', []);
  173.         
  174.         if (empty($viewedShopIds)) {
  175.             return [];
  176.         }
  177.         // Récupérer les dernières boutiques vues (ordre inverse)
  178.         $viewedShopIds array_reverse($viewedShopIds);
  179.         $viewedShopIds array_slice($viewedShopIds0$limit);
  180.         return $this->shopRepository->findBy(['id' => $viewedShopIds]);
  181.     }
  182.     /**
  183.      * Nettoie l'historique des vues
  184.      */
  185.     public function clearViewHistory(): void
  186.     {
  187.         $session $this->requestStack->getSession();
  188.         $session->remove('viewed_products');
  189.         $session->remove('viewed_shops');
  190.     }
  191.     /**
  192.      * Récupère les statistiques de vues pour un produit
  193.      */
  194.     public function getProductViewStats(Product $product): array
  195.     {
  196.         return [
  197.             'totalViews' => $product->getViewCount() ?? 0,
  198.             'salesCount' => $product->getSalesCount() ?? 0,
  199.             'conversionRate' => $this->calculateConversionRate($product),
  200.             'averageRating' => $product->getAverageRating() ?? 0,
  201.             'reviewCount' => $product->getReviewCount() ?? 0,
  202.         ];
  203.     }
  204.     /**
  205.      * Récupère les statistiques de vues pour une boutique
  206.      */
  207.     public function getShopViewStats(Shop $shop): array
  208.     {
  209.         $totalProductViews 0;
  210.         $totalProductSales 0;
  211.         
  212.         foreach ($shop->getProducts() as $product) {
  213.             $totalProductViews += $product->getViewCount() ?? 0;
  214.             $totalProductSales += $product->getSalesCount() ?? 0;
  215.         }
  216.         return [
  217.             'shopViews' => $shop->getViewCount() ?? 0,
  218.             'totalProductViews' => $totalProductViews,
  219.             'totalProductSales' => $totalProductSales,
  220.             'averageRating' => $shop->getAverageRating() ?? 0,
  221.             'reviewCount' => $shop->getReviewCount() ?? 0,
  222.             'activeProducts' => $shop->getActiveProductsCount(),
  223.             'conversionRate' => $this->calculateShopConversionRate($shop),
  224.         ];
  225.     }
  226.     /**
  227.      * Calcule le taux de conversion d'un produit
  228.      */
  229.     private function calculateConversionRate(Product $product): float
  230.     {
  231.         $views $product->getViewCount() ?? 0;
  232.         $sales $product->getSalesCount() ?? 0;
  233.         
  234.         if ($views === 0) {
  235.             return 0.0;
  236.         }
  237.         
  238.         return round(($sales $views) * 1002);
  239.     }
  240.     /**
  241.      * Calcule le taux de conversion d'une boutique
  242.      */
  243.     private function calculateShopConversionRate(Shop $shop): float
  244.     {
  245.         $totalViews 0;
  246.         $totalSales 0;
  247.         
  248.         foreach ($shop->getProducts() as $product) {
  249.             $totalViews += $product->getViewCount() ?? 0;
  250.             $totalSales += $product->getSalesCount() ?? 0;
  251.         }
  252.         
  253.         if ($totalViews === 0) {
  254.             return 0.0;
  255.         }
  256.         
  257.         return round(($totalSales $totalViews) * 1002);
  258.     }
  259. }