From 1f6aaf41e1b0d714976fb0771d5ca74993221bf8 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sat, 19 Feb 2022 23:33:19 +0100 Subject: [PATCH 01/18] add node view counter option --- core/Entity/Site/Node.php | 54 +++++++++++++++++++ core/Form/Site/NodeType.php | 13 +++++ core/Resources/translations/messages.fr.yaml | 2 + .../views/site/node_admin/_form.html.twig | 1 + 4 files changed, 70 insertions(+) diff --git a/core/Entity/Site/Node.php b/core/Entity/Site/Node.php index ab7d6ef..0c71538 100644 --- a/core/Entity/Site/Node.php +++ b/core/Entity/Site/Node.php @@ -4,6 +4,7 @@ namespace App\Core\Entity\Site; use App\Core\Doctrine\Timestampable; use App\Core\Entity\EntityInterface; +use App\Core\Entity\NodeView; use App\Core\Entity\Site\Page\Page; use App\Core\Repository\Site\NodeRepository; use Doctrine\Common\Collections\ArrayCollection; @@ -139,10 +140,21 @@ class Node implements EntityInterface */ protected $contentType; + /** + * @ORM\Column(type="boolean", options={"default"=0}) + */ + private $enableViewCounter = false; + + /** + * @ORM\OneToMany(targetEntity=NodeView::class, mappedBy="node", orphanRemoval=true) + */ + private $nodeViews; + public function __construct() { $this->children = new ArrayCollection(); $this->aliasNodes = new ArrayCollection(); + $this->nodeViews = new ArrayCollection(); } public function getId(): ?int @@ -548,4 +560,46 @@ class Node implements EntityInterface return $this; } + + public function getEnableViewCounter(): ?bool + { + return $this->enableViewCounter; + } + + public function setEnableViewCounter(bool $enableViewCounter): self + { + $this->enableViewCounter = $enableViewCounter; + + return $this; + } + + /** + * @return Collection|NodeView[] + */ + public function getNodeViews(): Collection + { + return $this->nodeViews; + } + + public function addNodeView(NodeView $nodeView): self + { + if (!$this->nodeViews->contains($nodeView)) { + $this->nodeViews[] = $nodeView; + $nodeView->setNode($this); + } + + return $this; + } + + public function removeNodeView(NodeView $nodeView): self + { + if ($this->nodeViews->removeElement($nodeView)) { + // set the owning side to null (unless already changed) + if ($nodeView->getNode() === $this) { + $nodeView->setNode(null); + } + } + + return $this; + } } diff --git a/core/Form/Site/NodeType.php b/core/Form/Site/NodeType.php index 5c9b314..184cf55 100644 --- a/core/Form/Site/NodeType.php +++ b/core/Form/Site/NodeType.php @@ -60,6 +60,19 @@ class NodeType extends AbstractType ] ); + $builder->add( + 'enableViewCounter', + CheckboxType::class, + [ + 'label' => 'Enable view counter', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); + $builder->add( 'code', TextType::class, diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index 0204b3e..26a9430 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -39,6 +39,8 @@ "Never": "Jamais" "URL": "URL" "Disable URL": "Désactiver l'URL" +"Enable view counter": "Activer le compteur de vues" +"Stats": "Statistiques" "Controller": "Contrôleur" "Leave blank for automatic generation": "Laisser vide pour une génération automatique" "Leave blank to use the default one. Example: App\\Controller\\FooController::barAction": "Laisser vide pour utiliser celui par défaut. Exemple : App\\Controller\\FooController::barAction" diff --git a/core/Resources/views/site/node_admin/_form.html.twig b/core/Resources/views/site/node_admin/_form.html.twig index 9fb460f..b1d7d78 100644 --- a/core/Resources/views/site/node_admin/_form.html.twig +++ b/core/Resources/views/site/node_admin/_form.html.twig @@ -205,6 +205,7 @@ {{ form_row(form.disableUrl) }} + {{ form_row(form.enableViewCounter) }} {{ form_row(form.code) }} {{ form_row(form.contentType) }} {{ form_row(form.controller) }} From 1cb57a138fb036b31fd26cafadaf3f9d0d2adba8 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sat, 19 Feb 2022 23:33:46 +0100 Subject: [PATCH 02/18] add node view entity --- core/Entity/NodeView.php | 102 ++++++++++++++++++++ core/Factory/NodeViewFactory.php | 21 ++++ core/Repository/NodeViewRepository.php | 21 ++++ core/Repository/NodeViewRepositoryQuery.php | 27 ++++++ 4 files changed, 171 insertions(+) create mode 100644 core/Entity/NodeView.php create mode 100644 core/Factory/NodeViewFactory.php create mode 100644 core/Repository/NodeViewRepository.php create mode 100644 core/Repository/NodeViewRepositoryQuery.php diff --git a/core/Entity/NodeView.php b/core/Entity/NodeView.php new file mode 100644 index 0000000..506dbb5 --- /dev/null +++ b/core/Entity/NodeView.php @@ -0,0 +1,102 @@ +id; + } + + public function getNode(): ?Node + { + return $this->node; + } + + public function setNode(?Node $node): self + { + $this->node = $node; + + return $this; + } + + public function getPath(): ?string + { + return $this->path; + } + + public function setPath(string $path): self + { + $this->path = $path; + + return $this; + } + + public function getViews(): ?int + { + return $this->views; + } + + public function setViews(int $views): self + { + $this->views = $views; + + return $this; + } + + public function addView(): self + { + ++$this->views; + + return $this; + } + + public function getDate(): ?\DateTimeInterface + { + return $this->date; + } + + public function setDate(\DateTimeInterface $date): self + { + $this->date = $date; + + return $this; + } +} diff --git a/core/Factory/NodeViewFactory.php b/core/Factory/NodeViewFactory.php new file mode 100644 index 0000000..ecc3862 --- /dev/null +++ b/core/Factory/NodeViewFactory.php @@ -0,0 +1,21 @@ +setNode($node) + ->setPath($path) + ->setDate(new \DateTime()) + ; + + return $entity; + } +} diff --git a/core/Repository/NodeViewRepository.php b/core/Repository/NodeViewRepository.php new file mode 100644 index 0000000..5175e58 --- /dev/null +++ b/core/Repository/NodeViewRepository.php @@ -0,0 +1,21 @@ +andWhere('.node = :node') + ->andWhere('.path = :path') + ->setParameters([ + ':node' => $request->attributes->get('_node'), + ':path' => $request->getPathInfo(), + ]) + ; + } +} From 98456ab8e5752e452f3e7d95ed5bfc68897a42eb Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sat, 19 Feb 2022 23:35:41 +0100 Subject: [PATCH 03/18] add node view listener --- config/services.yaml | 4 ++ core/EventListener/NodeViewListener.php | 68 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core/EventListener/NodeViewListener.php diff --git a/config/services.yaml b/config/services.yaml index 88fe2f6..b81dbd3 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,6 +23,10 @@ services: tags: - { name: kernel.event_listener, event: kernel.exception } + App\Core\EventListener\NodeViewListener: + tags: + - { name: kernel.event_listener, event: kernel.request } + App\: resource: '../src/' exclude: diff --git a/core/EventListener/NodeViewListener.php b/core/EventListener/NodeViewListener.php new file mode 100644 index 0000000..52b3880 --- /dev/null +++ b/core/EventListener/NodeViewListener.php @@ -0,0 +1,68 @@ + + */ +class NodeViewListener +{ + protected NodeRepository $nodeRepository; + protected NodeViewRepositoryQuery $nodeViewRepositoryQuery; + protected NodeViewFactory $nodeViewFactory; + protected EntityManager $manager; + + public function __construct( + NodeRepository $nodeRepository, + NodeViewRepositoryQuery $nodeViewRepositoryQuery, + NodeViewFactory $nodeViewFactory, + EntityManager $manager + ) { + $this->nodeRepository = $nodeRepository; + $this->nodeViewRepositoryQuery = $nodeViewRepositoryQuery; + $this->nodeViewFactory = $nodeViewFactory; + $this->manager = $manager; + } + + public function onKernelRequest(RequestEvent $event) + { + $request = $event->getRequest(); + + if (!$request->attributes->has('_node')) { + return; + } + + $node = $this->nodeRepository->findOneById($request->attributes->get('_node')); + + if (!$node || !$node->getEnableViewCounter()) { + return; + } + + $nodeView = $this->nodeViewRepositoryQuery->create() + ->filterByRequest($request) + ->andWhere('.date=CURRENT_DATE()') + ->findOne() + ; + + if (!$nodeView) { + $nodeView = $this->nodeViewFactory->create($node, $request->getPathInfo()); + } + + $nodeView->addView(); + + if ($nodeView->getId()) { + $this->manager->update($nodeView); + } else { + $this->manager->create($nodeView); + } + } +} From 58c8b6126ebb045fa2e1390d6732bedec90be9c4 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 13:08:54 +0100 Subject: [PATCH 04/18] remove useless class --- core/Site/Node.php | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 core/Site/Node.php diff --git a/core/Site/Node.php b/core/Site/Node.php deleted file mode 100644 index ec63344..0000000 --- a/core/Site/Node.php +++ /dev/null @@ -1,24 +0,0 @@ -id; - } -} From ec4b3341c87526d4efa087c164476376ca74467d Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 15:25:07 +0100 Subject: [PATCH 05/18] - move NodeView to Analytic/View - move NodeViewListener to AnalyticListener - add referer --- .gitignore | 2 + composer.json | 1 + config/services.yaml | 2 +- core/Entity/Analytic/Referer.php | 103 +++++++++++++++ .../{NodeView.php => Analytic/View.php} | 11 +- core/Entity/Site/Node.php | 68 +++++++--- core/EventListener/AnalyticListener.php | 124 ++++++++++++++++++ core/EventListener/NodeViewListener.php | 68 ---------- core/Factory/Analytic/RefererFactory.php | 22 ++++ .../ViewFactory.php} | 7 +- .../Repository/Analytic/RefererRepository.php | 21 +++ .../Analytic/RefererRepositoryQuery.php | 28 ++++ core/Repository/Analytic/ViewRepository.php | 21 +++ .../ViewRepositoryQuery.php} | 7 +- core/Repository/NodeViewRepository.php | 21 --- migrations/.gitignore | 0 symfony.lock | 3 + 17 files changed, 393 insertions(+), 116 deletions(-) create mode 100644 core/Entity/Analytic/Referer.php rename core/Entity/{NodeView.php => Analytic/View.php} (84%) create mode 100644 core/EventListener/AnalyticListener.php delete mode 100644 core/EventListener/NodeViewListener.php create mode 100644 core/Factory/Analytic/RefererFactory.php rename core/Factory/{NodeViewFactory.php => Analytic/ViewFactory.php} (64%) create mode 100644 core/Repository/Analytic/RefererRepository.php create mode 100644 core/Repository/Analytic/RefererRepositoryQuery.php create mode 100644 core/Repository/Analytic/ViewRepository.php rename core/Repository/{NodeViewRepositoryQuery.php => Analytic/ViewRepositoryQuery.php} (75%) delete mode 100644 core/Repository/NodeViewRepository.php delete mode 100644 migrations/.gitignore diff --git a/.gitignore b/.gitignore index 18ffc85..c0aa6b0 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ yarn-error.log /public/uploads/ !/public/uploads/.gitkeep /public/media/ +/migrations/ +!/migrations/.gitkeep diff --git a/composer.json b/composer.json index 266ff76..dc97ae6 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/orm": "^2.8", "friendsofsymfony/jsrouting-bundle": "^2.7", + "jaybizzle/crawler-detect": "^1.2", "knplabs/doctrine-behaviors": "^2.2", "knplabs/knp-paginator-bundle": "^5.8", "liip/imagine-bundle": "^2.6", diff --git a/config/services.yaml b/config/services.yaml index b81dbd3..4d04c70 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,7 +23,7 @@ services: tags: - { name: kernel.event_listener, event: kernel.exception } - App\Core\EventListener\NodeViewListener: + App\Core\EventListener\AnalyticListener: tags: - { name: kernel.event_listener, event: kernel.request } diff --git a/core/Entity/Analytic/Referer.php b/core/Entity/Analytic/Referer.php new file mode 100644 index 0000000..0281d3c --- /dev/null +++ b/core/Entity/Analytic/Referer.php @@ -0,0 +1,103 @@ +id; + } + + public function getNode(): ?Node + { + return $this->node; + } + + public function setNode(?Node $node): self + { + $this->node = $node; + + return $this; + } + + public function getUri(): ?string + { + return $this->uri; + } + + public function setUri(string $uri): self + { + $this->uri = $uri; + + return $this; + } + + public function getViews(): ?int + { + return $this->views; + } + + public function setViews(int $views): self + { + $this->views = $views; + + return $this; + } + + public function addView(): self + { + ++$this->views; + + return $this; + } + + public function getDate(): ?\DateTimeInterface + { + return $this->date; + } + + public function setDate(\DateTimeInterface $date): self + { + $this->date = $date; + + return $this; + } +} diff --git a/core/Entity/NodeView.php b/core/Entity/Analytic/View.php similarity index 84% rename from core/Entity/NodeView.php rename to core/Entity/Analytic/View.php index 506dbb5..a5db1b9 100644 --- a/core/Entity/NodeView.php +++ b/core/Entity/Analytic/View.php @@ -1,16 +1,17 @@ children = new ArrayCollection(); $this->aliasNodes = new ArrayCollection(); - $this->nodeViews = new ArrayCollection(); + $this->analyticViews = new ArrayCollection(); + $this->analyticReferers = new ArrayCollection(); } public function getId(): ?int @@ -574,29 +582,59 @@ class Node implements EntityInterface } /** - * @return Collection|NodeView[] + * @return Collection|View[] */ - public function getNodeViews(): Collection + public function getAnalyticViews(): Collection { - return $this->nodeViews; + return $this->analyticViews; } - public function addNodeView(NodeView $nodeView): self + public function addAnalyticView(View $view): self { - if (!$this->nodeViews->contains($nodeView)) { - $this->nodeViews[] = $nodeView; - $nodeView->setNode($this); + if (!$this->analyticViews->contains($view)) { + $this->analyticViews[] = $view; + $view->setNode($this); } return $this; } - public function removeNodeView(NodeView $nodeView): self + public function removeAnalyticView(View $view): self { - if ($this->nodeViews->removeElement($nodeView)) { + if ($this->analyticViews->removeElement($view)) { // set the owning side to null (unless already changed) - if ($nodeView->getNode() === $this) { - $nodeView->setNode(null); + if ($view->getNode() === $this) { + $view->setNode(null); + } + } + + return $this; + } + + /** + * @return Collection|Referer[] + */ + public function getAnalyticReferers(): Collection + { + return $this->analyticReferers; + } + + public function addAnalyticReferer(Referer $referer): self + { + if (!$this->analyticReferers->contains($referer)) { + $this->analyticReferers[] = $referer; + $referer->setNode($this); + } + + return $this; + } + + public function removeAnalyticReferer(Referer $referer): self + { + if ($this->analyticReferers->removeElement($referer)) { + // set the owning side to null (unless already changed) + if ($referer->getNode() === $this) { + $referer->setNode(null); } } diff --git a/core/EventListener/AnalyticListener.php b/core/EventListener/AnalyticListener.php new file mode 100644 index 0000000..eb6b983 --- /dev/null +++ b/core/EventListener/AnalyticListener.php @@ -0,0 +1,124 @@ + + */ +class AnalyticListener +{ + protected NodeRepository $nodeRepository; + protected ViewRepositoryQuery $viewRepositoryQuery; + protected ViewFactory $viewFactory; + protected RefererRepositoryQuery $refererRepositoryQuery; + protected RefererFactory $refererFactory; + protected EntityManager $manager; + protected CrawlerDetect $crawlerDetect; + protected Request $request; + protected Node $node; + + public function __construct( + NodeRepository $nodeRepository, + ViewRepositoryQuery $viewRepositoryQuery, + ViewFactory $viewFactory, + RefererRepositoryQuery $refererRepositoryQuery, + RefererFactory $refererFactory, + EntityManager $manager + ) { + $this->nodeRepository = $nodeRepository; + $this->viewRepositoryQuery = $viewRepositoryQuery; + $this->viewFactory = $viewFactory; + $this->refererRepositoryQuery = $refererRepositoryQuery; + $this->refererFactory = $refererFactory; + $this->manager = $manager; + $this->crawlerDetect = new CrawlerDetect(); + } + + public function onKernelRequest(RequestEvent $event) + { + $request = $event->getRequest(); + + if (!$request->attributes->has('_node')) { + return; + } + + if ($this->crawlerDetect->isCrawler($request->headers->get('user-agent'))) { + return; + } + + $node = $this->nodeRepository->findOneBy([ + 'id' => $request->attributes->get('_node'), + 'enableViewCounter' => true, + ]); + + if (!$node) { + return; + } + + $this->node = $node; + $this->request = $request; + + $this->createView(); + $this->createReferer(); + } + + protected function createView() + { + $entity = $this->viewRepositoryQuery->create() + ->filterByRequest($this->request) + ->andWhere('.date=CURRENT_DATE()') + ->findOne() + ; + + if (!$entity) { + $entity = $this->viewFactory->create($this->node, $this->request->getPathInfo()); + } + + $entity->addView(); + $this->save($entity); + } + + protected function createReferer() + { + if (!$this->request->headers->has('referer')) { + return; + } + + $entity = $this->refererRepositoryQuery->create() + ->filterByRequest($this->request) + ->andWhere('.date=CURRENT_DATE()') + ->findOne() + ; + + if (!$entity) { + $entity = $this->refererFactory->create($this->node, $this->request->headers->get('referer')); + } + + $entity->addView(); + $this->save($entity); + } + + protected function save(EntityInterface $entity) + { + if ($entity->getId()) { + $this->manager->update($entity); + } else { + $this->manager->create($entity); + } + } +} diff --git a/core/EventListener/NodeViewListener.php b/core/EventListener/NodeViewListener.php deleted file mode 100644 index 52b3880..0000000 --- a/core/EventListener/NodeViewListener.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ -class NodeViewListener -{ - protected NodeRepository $nodeRepository; - protected NodeViewRepositoryQuery $nodeViewRepositoryQuery; - protected NodeViewFactory $nodeViewFactory; - protected EntityManager $manager; - - public function __construct( - NodeRepository $nodeRepository, - NodeViewRepositoryQuery $nodeViewRepositoryQuery, - NodeViewFactory $nodeViewFactory, - EntityManager $manager - ) { - $this->nodeRepository = $nodeRepository; - $this->nodeViewRepositoryQuery = $nodeViewRepositoryQuery; - $this->nodeViewFactory = $nodeViewFactory; - $this->manager = $manager; - } - - public function onKernelRequest(RequestEvent $event) - { - $request = $event->getRequest(); - - if (!$request->attributes->has('_node')) { - return; - } - - $node = $this->nodeRepository->findOneById($request->attributes->get('_node')); - - if (!$node || !$node->getEnableViewCounter()) { - return; - } - - $nodeView = $this->nodeViewRepositoryQuery->create() - ->filterByRequest($request) - ->andWhere('.date=CURRENT_DATE()') - ->findOne() - ; - - if (!$nodeView) { - $nodeView = $this->nodeViewFactory->create($node, $request->getPathInfo()); - } - - $nodeView->addView(); - - if ($nodeView->getId()) { - $this->manager->update($nodeView); - } else { - $this->manager->create($nodeView); - } - } -} diff --git a/core/Factory/Analytic/RefererFactory.php b/core/Factory/Analytic/RefererFactory.php new file mode 100644 index 0000000..8884338 --- /dev/null +++ b/core/Factory/Analytic/RefererFactory.php @@ -0,0 +1,22 @@ +setNode($node) + ->setUri($uri) + ->setDate(new \DateTime()) + ; + + return $entity; + } +} diff --git a/core/Factory/NodeViewFactory.php b/core/Factory/Analytic/ViewFactory.php similarity index 64% rename from core/Factory/NodeViewFactory.php rename to core/Factory/Analytic/ViewFactory.php index ecc3862..840c7ad 100644 --- a/core/Factory/NodeViewFactory.php +++ b/core/Factory/Analytic/ViewFactory.php @@ -1,11 +1,12 @@ andWhere('.node = :node') + ->andWhere('.uri = :uri') + ->setParameters([ + ':node' => $request->attributes->get('_node'), + ':uri' => $request->headers->get('referer'), + ]) + ; + } +} diff --git a/core/Repository/Analytic/ViewRepository.php b/core/Repository/Analytic/ViewRepository.php new file mode 100644 index 0000000..4ddf0a3 --- /dev/null +++ b/core/Repository/Analytic/ViewRepository.php @@ -0,0 +1,21 @@ + Date: Sun, 20 Feb 2022 16:12:22 +0100 Subject: [PATCH 06/18] rename enableViewCounter with enableAnalytic --- core/Entity/Site/Node.php | 10 +++++----- core/Form/Site/NodeType.php | 4 ++-- core/Resources/translations/messages.fr.yaml | 3 +-- core/Resources/views/site/node_admin/_form.html.twig | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/core/Entity/Site/Node.php b/core/Entity/Site/Node.php index bfe78da..35a684e 100644 --- a/core/Entity/Site/Node.php +++ b/core/Entity/Site/Node.php @@ -145,7 +145,7 @@ class Node implements EntityInterface /** * @ORM\Column(type="boolean", options={"default"=0}) */ - protected $enableViewCounter = false; + protected $enableAnalytic = false; /** * @ORM\OneToMany(targetEntity=View::class, mappedBy="node") @@ -569,14 +569,14 @@ class Node implements EntityInterface return $this; } - public function getEnableViewCounter(): ?bool + public function getEnableAnalytic(): ?bool { - return $this->enableViewCounter; + return $this->enableAnalytic; } - public function setEnableViewCounter(bool $enableViewCounter): self + public function setEnableAnalytic(bool $enableAnalytic): self { - $this->enableViewCounter = $enableViewCounter; + $this->enableAnalytic = $enableAnalytic; return $this; } diff --git a/core/Form/Site/NodeType.php b/core/Form/Site/NodeType.php index 184cf55..4f41c1d 100644 --- a/core/Form/Site/NodeType.php +++ b/core/Form/Site/NodeType.php @@ -61,10 +61,10 @@ class NodeType extends AbstractType ); $builder->add( - 'enableViewCounter', + 'enableAnalytic', CheckboxType::class, [ - 'label' => 'Enable view counter', + 'label' => 'Enable analytic', 'required' => false, 'attr' => [ ], diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index 26a9430..7f02595 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -39,8 +39,7 @@ "Never": "Jamais" "URL": "URL" "Disable URL": "Désactiver l'URL" -"Enable view counter": "Activer le compteur de vues" -"Stats": "Statistiques" +"Enable analytic": "Activer l'analyse" "Controller": "Contrôleur" "Leave blank for automatic generation": "Laisser vide pour une génération automatique" "Leave blank to use the default one. Example: App\\Controller\\FooController::barAction": "Laisser vide pour utiliser celui par défaut. Exemple : App\\Controller\\FooController::barAction" diff --git a/core/Resources/views/site/node_admin/_form.html.twig b/core/Resources/views/site/node_admin/_form.html.twig index b1d7d78..44064ce 100644 --- a/core/Resources/views/site/node_admin/_form.html.twig +++ b/core/Resources/views/site/node_admin/_form.html.twig @@ -205,7 +205,7 @@ {{ form_row(form.disableUrl) }} - {{ form_row(form.enableViewCounter) }} + {{ form_row(form.enableAnalytic) }} {{ form_row(form.code) }} {{ form_row(form.contentType) }} {{ form_row(form.controller) }} From 23ee09b1b5e658e2fdacb3882e3b9035efcc6dd0 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 21:05:18 +0100 Subject: [PATCH 07/18] NodeAdminController now extends AbstractController --- core/Controller/Site/NodeAdminController.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/Controller/Site/NodeAdminController.php b/core/Controller/Site/NodeAdminController.php index 267cf68..327bd34 100644 --- a/core/Controller/Site/NodeAdminController.php +++ b/core/Controller/Site/NodeAdminController.php @@ -21,11 +21,12 @@ use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; /** * @Route("/admin/site/node") */ -class NodeAdminController extends AdminController +class NodeAdminController extends AbstractController { /** * @Route("/new/{node}", name="admin_site_node_new") @@ -268,11 +269,6 @@ class NodeAdminController extends AdminController ]); } - public function getSection(): string - { - return ''; - } - protected function handlePageAssociation( string $pageAction, ?Page $pageEntity, From e67966df4518e2271badbb1f6ef16fa63d5a96c6 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 21:05:46 +0100 Subject: [PATCH 08/18] add analytic chart and table --- core/Analytic/RangeAnalytic.php | 133 ++++++++++++++++++ .../Analytic/AnalyticController.php | 37 +++++ core/Entity/Analytic/Referer.php | 10 +- core/Entity/Analytic/View.php | 10 +- core/Entity/Site/Node.php | 10 +- core/EventListener/AnalyticListener.php | 2 +- core/Form/Site/NodeType.php | 4 +- core/Resources/translations/messages.fr.yaml | 3 +- core/Resources/views/analytic/stats.html.twig | 111 +++++++++++++++ .../site/tree_admin/navigation.html.twig | 5 + 10 files changed, 306 insertions(+), 19 deletions(-) create mode 100644 core/Analytic/RangeAnalytic.php create mode 100644 core/Controller/Analytic/AnalyticController.php create mode 100644 core/Resources/views/analytic/stats.html.twig diff --git a/core/Analytic/RangeAnalytic.php b/core/Analytic/RangeAnalytic.php new file mode 100644 index 0000000..a4d5ef6 --- /dev/null +++ b/core/Analytic/RangeAnalytic.php @@ -0,0 +1,133 @@ + + */ +class RangeAnalytic +{ + protected ViewRepositoryQuery $viewQuery; + protected RefererRepositoryQuery $refererQuery; + + public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery) + { + $this->viewQuery = $viewQuery; + $this->refererQuery = $refererQuery; + } + + public function getViews(\DateTime $from, \DateTime $to, Node $node): array + { + $entities = $this->viewQuery->create() + ->andWhere('.date >= :from') + ->andWhere('.date <= :to') + ->andWhere('.node = :node') + ->orderBy('.date') + ->setParameters([ + ':from' => $from, + ':to' => $to, + ':node' => $node->getId(), + ]) + ->find() + ; + + $diff = $from->diff($to); + + if ($diff->days >= 365) { + $format = 'Y-m'; + } else { + $format = 'Y-m-d'; + } + + $datas = []; + + foreach ($entities as $entity) { + $index = $entity->getDate()->format($format); + + if (!isset($datas[$index])) { + $datas[$index] = 0; + } + + $datas[$index] += $entity->getViews(); + } + + return $datas; + } + + public function getPathViews(\DateTime $from, \DateTime $to, Node $node): array + { + $entities = $this->viewQuery->create() + ->andWhere('.date >= :from') + ->andWhere('.date <= :to') + ->andWhere('.node = :node') + ->orderBy('.date') + ->setParameters([ + ':from' => $from, + ':to' => $to, + ':node' => $node->getId(), + ]) + ->find() + ; + + $datas = []; + + foreach ($entities as $entity) { + $index = $entity->getPath(); + + if (!isset($datas[$index])) { + $datas[$index] = 0; + } + + $datas[$index] += $entity->getViews(); + } + + return $datas; + } + + public function getReferers(\DateTime $from, \DateTime $to, Node $node): array + { + $entities = $this->refererQuery->create() + ->andWhere('.date >= :from') + ->andWhere('.date <= :to') + ->andWhere('.node = :node') + ->orderBy('.date') + ->setParameters([ + ':from' => $from, + ':to' => $to, + ':node' => $node->getId(), + ]) + ->find() + ; + + $datas = []; + + foreach ($entities as $entity) { + $index = parse_url($entity->getUri(), PHP_URL_HOST); + + if (!isset($datas[$index])) { + $datas[$index] = [ + 'views' => 0, + 'uris' => [], + ]; + } + + $datas[$index]['views'] += $entity->getViews(); + + $path = parse_url($entity->getUri(), PHP_URL_PATH); + + if (!isset($datas[$index]['uris'][$path])) { + $datas[$index]['uris'][$path] = 0; + } + + $datas[$index]['uris'][$path] += $entity->getViews(); + } + + return $datas; + } +} diff --git a/core/Controller/Analytic/AnalyticController.php b/core/Controller/Analytic/AnalyticController.php new file mode 100644 index 0000000..83b9640 --- /dev/null +++ b/core/Controller/Analytic/AnalyticController.php @@ -0,0 +1,37 @@ +createNotFoundException(); + } + + $views = $rangeAnalytic->getViews(new \DateTime('now - '.$range), new \DateTime(), $node); + $pathViews = $rangeAnalytic->getPathViews(new \DateTime('now - '.$range), new \DateTime(), $node); + $referers = $rangeAnalytic->getReferers(new \DateTime('now - '.$range), new \DateTime(), $node); + + return $this->render('@Core/analytic/stats.html.twig', [ + 'range' => $range, + 'views' => $views, + 'pathViews' => $pathViews, + 'referers' => $referers, + 'node' => $node, + ]); + } +} diff --git a/core/Entity/Analytic/Referer.php b/core/Entity/Analytic/Referer.php index 0281d3c..1c8b94d 100644 --- a/core/Entity/Analytic/Referer.php +++ b/core/Entity/Analytic/Referer.php @@ -18,28 +18,28 @@ class Referer implements EntityInterface * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + protected $id; /** * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private $node; + protected $node; /** * @ORM\Column(type="string", length=255) */ - private $uri; + protected $uri; /** * @ORM\Column(type="integer", options={"default"=0}) */ - private $views = 0; + protected $views = 0; /** * @ORM\Column(type="date") */ - private $date; + protected $date; public function getId(): ?int { diff --git a/core/Entity/Analytic/View.php b/core/Entity/Analytic/View.php index a5db1b9..0bd3338 100644 --- a/core/Entity/Analytic/View.php +++ b/core/Entity/Analytic/View.php @@ -18,28 +18,28 @@ class View implements EntityInterface * @ORM\GeneratedValue * @ORM\Column(type="integer") */ - private $id; + protected $id; /** * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private $node; + protected $node; /** * @ORM\Column(type="string", length=255) */ - private $path; + protected $path; /** * @ORM\Column(type="integer", options={"default"=0}) */ - private $views = 0; + protected $views = 0; /** * @ORM\Column(type="date") */ - private $date; + protected $date; public function getId(): ?int { diff --git a/core/Entity/Site/Node.php b/core/Entity/Site/Node.php index 35a684e..4196966 100644 --- a/core/Entity/Site/Node.php +++ b/core/Entity/Site/Node.php @@ -145,7 +145,7 @@ class Node implements EntityInterface /** * @ORM\Column(type="boolean", options={"default"=0}) */ - protected $enableAnalytic = false; + protected $enableAnalytics = false; /** * @ORM\OneToMany(targetEntity=View::class, mappedBy="node") @@ -569,14 +569,14 @@ class Node implements EntityInterface return $this; } - public function getEnableAnalytic(): ?bool + public function getEnableAnalytics(): ?bool { - return $this->enableAnalytic; + return $this->enableAnalytics; } - public function setEnableAnalytic(bool $enableAnalytic): self + public function setEnableAnalytics(bool $enableAnalytics): self { - $this->enableAnalytic = $enableAnalytic; + $this->enableAnalytics = $enableAnalytics; return $this; } diff --git a/core/EventListener/AnalyticListener.php b/core/EventListener/AnalyticListener.php index eb6b983..6154378 100644 --- a/core/EventListener/AnalyticListener.php +++ b/core/EventListener/AnalyticListener.php @@ -63,7 +63,7 @@ class AnalyticListener $node = $this->nodeRepository->findOneBy([ 'id' => $request->attributes->get('_node'), - 'enableViewCounter' => true, + 'enableAnalytics' => true, ]); if (!$node) { diff --git a/core/Form/Site/NodeType.php b/core/Form/Site/NodeType.php index 4f41c1d..284d0f9 100644 --- a/core/Form/Site/NodeType.php +++ b/core/Form/Site/NodeType.php @@ -61,10 +61,10 @@ class NodeType extends AbstractType ); $builder->add( - 'enableAnalytic', + 'enableAnalytics', CheckboxType::class, [ - 'label' => 'Enable analytic', + 'label' => 'Enable analytics', 'required' => false, 'attr' => [ ], diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index 7f02595..039b2cc 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -39,7 +39,8 @@ "Never": "Jamais" "URL": "URL" "Disable URL": "Désactiver l'URL" -"Enable analytic": "Activer l'analyse" +"Enable analytics": "Activer les analyses" +"Analytics": "Analyses" "Controller": "Contrôleur" "Leave blank for automatic generation": "Laisser vide pour une génération automatique" "Leave blank to use the default one. Example: App\\Controller\\FooController::barAction": "Laisser vide pour utiliser celui par défaut. Exemple : App\\Controller\\FooController::barAction" diff --git a/core/Resources/views/analytic/stats.html.twig b/core/Resources/views/analytic/stats.html.twig new file mode 100644 index 0000000..b1bf18d --- /dev/null +++ b/core/Resources/views/analytic/stats.html.twig @@ -0,0 +1,111 @@ + diff --git a/core/Resources/views/site/tree_admin/navigation.html.twig b/core/Resources/views/site/tree_admin/navigation.html.twig index b077da6..f49a211 100644 --- a/core/Resources/views/site/tree_admin/navigation.html.twig +++ b/core/Resources/views/site/tree_admin/navigation.html.twig @@ -72,6 +72,7 @@ {% set move = path('admin_site_node_move', {entity: node.id}) %} {% set edit = path('admin_site_node_edit', {entity: node.id}) %} {% set new = path('admin_site_node_new', {node: node.id}) %} + {% set analytics = path('admin_analytic_stats', {node: node.id}) %}
@@ -147,6 +148,10 @@ {% endif %} + + From 9d2048094ff5e161a66b49fcb61547cb106ba58e Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 21:05:53 +0100 Subject: [PATCH 09/18] add chartjs --- package.json | 1 + yarn.lock | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 65c9408..25ed802 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@fortawesome/fontawesome-free": "^5.11.2", "axios": "^0.21.1", "bootstrap": "^4.3.1", + "chart.js": "^3.7.1", "choices.js": "^9.0.1", "flag-icon-css": "^3.5.0", "jquery": "^3.6.0", diff --git a/yarn.lock b/yarn.lock index fae2b28..6faa9fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1819,6 +1819,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chart.js@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.7.1.tgz#0516f690c6a8680c6c707e31a4c1807a6f400ada" + integrity sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA== + choices.js@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/choices.js/-/choices.js-9.0.1.tgz#745fb29af8670428fdc0bf1cc9dfaa404e9d0510" @@ -2048,9 +2053,9 @@ core-js-compat@^3.8.1, core-js-compat@^3.9.0: semver "7.0.0" core-js@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" - integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" + integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== core-util-is@~1.0.0: version "1.0.2" From 690e117b0b6f2a010f953de6cd6b2270e6a18a5d Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 21:08:14 +0100 Subject: [PATCH 10/18] trigger events when modal content is reloaded --- assets/js/admin/modules/modal.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/js/admin/modules/modal.js b/assets/js/admin/modules/modal.js index 8afb28a..13cecba 100644 --- a/assets/js/admin/modules/modal.js +++ b/assets/js/admin/modules/modal.js @@ -3,8 +3,10 @@ const $ = require('jquery') const openModal = function (url) { let container = $('#modal-container') const body = $('body') + let doTrigger = true if (!container.length) { + let doTrigger = false container = $(' {{ form_row(form.disableUrl) }} - {{ form_row(form.enableAnalytic) }} + {{ form_row(form.enableAnalytics) }} {{ form_row(form.code) }} {{ form_row(form.contentType) }} {{ form_row(form.controller) }} diff --git a/core/Resources/views/site/tree_admin/navigation.html.twig b/core/Resources/views/site/tree_admin/navigation.html.twig index f49a211..31efad1 100644 --- a/core/Resources/views/site/tree_admin/navigation.html.twig +++ b/core/Resources/views/site/tree_admin/navigation.html.twig @@ -148,9 +148,11 @@ {% endif %} - + {% if node.enableAnalytics %} + + {% endif %} + {% if node.enableAnalytics %} + + {% endif %} + From 67694ffe8c6a7e1c482a1e717b08f8ac586aa4a0 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 23:04:44 +0100 Subject: [PATCH 14/18] add no result when views and referers are empty --- core/Resources/views/analytic/stats.html.twig | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/Resources/views/analytic/stats.html.twig b/core/Resources/views/analytic/stats.html.twig index b1bf18d..8886d4a 100644 --- a/core/Resources/views/analytic/stats.html.twig +++ b/core/Resources/views/analytic/stats.html.twig @@ -56,6 +56,17 @@ {{ path }} {{ views }} + {% else %} + + +
+ +
+
+ {{ 'No result'|trans }} +
+ + {% endfor %} @@ -96,6 +107,17 @@ + {% else %} + + +
+ +
+
+ {{ 'No result'|trans }} +
+ + {% endfor %} From 914fef625a2f72a8e19b03bdb4dea5f3d937e28e Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 23:06:39 +0100 Subject: [PATCH 15/18] update translations --- core/Resources/translations/messages.fr.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index 039b2cc..79033d2 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -41,6 +41,8 @@ "Disable URL": "Désactiver l'URL" "Enable analytics": "Activer les analyses" "Analytics": "Analyses" +"Referers": "Référents" +"Views": "Vues" "Controller": "Contrôleur" "Leave blank for automatic generation": "Laisser vide pour une génération automatique" "Leave blank to use the default one. Example: App\\Controller\\FooController::barAction": "Laisser vide pour utiliser celui par défaut. Exemple : App\\Controller\\FooController::barAction" From 6c75f8ffc3c2aa7f907a01eb6f687f28ab6133b2 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 20 Feb 2022 23:08:27 +0100 Subject: [PATCH 16/18] update translations --- core/Resources/translations/messages.fr.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/Resources/translations/messages.fr.yaml b/core/Resources/translations/messages.fr.yaml index 79033d2..83e5f44 100644 --- a/core/Resources/translations/messages.fr.yaml +++ b/core/Resources/translations/messages.fr.yaml @@ -43,6 +43,10 @@ "Analytics": "Analyses" "Referers": "Référents" "Views": "Vues" +"Last 7 days": "Les 7 derniers jours" +"Last 30 days": "Les 30 derniers jours" +"Last 90 days": "Les 90 derniers jours" +"Last year": "L'année passée" "Controller": "Contrôleur" "Leave blank for automatic generation": "Laisser vide pour une génération automatique" "Leave blank to use the default one. Example: App\\Controller\\FooController::barAction": "Laisser vide pour utiliser celui par défaut. Exemple : App\\Controller\\FooController::barAction" From 0739f683c7ef01da9e7237899e1a2856aa9a08b0 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Mon, 21 Feb 2022 09:09:57 +0100 Subject: [PATCH 17/18] refactoring of RangeAnalytic --- core/Analytic/RangeAnalytic.php | 127 +++++++++++------- .../Analytic/AnalyticController.php | 17 +-- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/core/Analytic/RangeAnalytic.php b/core/Analytic/RangeAnalytic.php index a4d5ef6..c4a0f92 100644 --- a/core/Analytic/RangeAnalytic.php +++ b/core/Analytic/RangeAnalytic.php @@ -2,9 +2,9 @@ namespace App\Core\Analytic; +use App\Core\Entity\Site\Node; use App\Core\Repository\Analytic\RefererRepositoryQuery; use App\Core\Repository\Analytic\ViewRepositoryQuery; -use App\Core\Entity\Site\Node; /** * class RangeAnalytic. @@ -15,6 +15,11 @@ class RangeAnalytic { protected ViewRepositoryQuery $viewQuery; protected RefererRepositoryQuery $refererQuery; + protected ?Node $node; + protected ?\DateTime $from; + protected ?\DateTime $to; + protected bool $reload = true; + protected array $cache = []; public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery) { @@ -22,27 +27,22 @@ class RangeAnalytic $this->refererQuery = $refererQuery; } - public function getViews(\DateTime $from, \DateTime $to, Node $node): array + public function getViews(): array { - $entities = $this->viewQuery->create() - ->andWhere('.date >= :from') - ->andWhere('.date <= :to') - ->andWhere('.node = :node') - ->orderBy('.date') - ->setParameters([ - ':from' => $from, - ':to' => $to, - ':node' => $node->getId(), - ]) - ->find() - ; + $entities = $this->getEntities('view'); + $this->reload = false; - $diff = $from->diff($to); + if ($entities) { + $first = $entities[0]; + $last = $entities[count($entities) - 1]; - if ($diff->days >= 365) { - $format = 'Y-m'; - } else { - $format = 'Y-m-d'; + $diff = $first->getDate()->diff($last->getDate()); + + if ($diff->days >= 90) { + $format = 'Y-m'; + } else { + $format = 'Y-m-d'; + } } $datas = []; @@ -60,20 +60,10 @@ class RangeAnalytic return $datas; } - public function getPathViews(\DateTime $from, \DateTime $to, Node $node): array + public function getPathViews(): array { - $entities = $this->viewQuery->create() - ->andWhere('.date >= :from') - ->andWhere('.date <= :to') - ->andWhere('.node = :node') - ->orderBy('.date') - ->setParameters([ - ':from' => $from, - ':to' => $to, - ':node' => $node->getId(), - ]) - ->find() - ; + $entities = $this->getEntities('view'); + $this->reload = false; $datas = []; @@ -90,20 +80,10 @@ class RangeAnalytic return $datas; } - public function getReferers(\DateTime $from, \DateTime $to, Node $node): array + public function getReferers(): array { - $entities = $this->refererQuery->create() - ->andWhere('.date >= :from') - ->andWhere('.date <= :to') - ->andWhere('.node = :node') - ->orderBy('.date') - ->setParameters([ - ':from' => $from, - ':to' => $to, - ':node' => $node->getId(), - ]) - ->find() - ; + $entities = $this->getEntities('referer'); + $this->reload = false; $datas = []; @@ -130,4 +110,61 @@ class RangeAnalytic return $datas; } + + public function setDateRange(?\DateTime $from, ?\DateTime $to): self + { + $this->from = $from; + $this->to = $to; + $this->reload = true; + + return $this; + } + + public function setNode(?Node $node): self + { + $this->node = $node; + $this->reload = true; + + return $this; + } + + protected function getEntities(string $type): array + { + if ('view' === $type) { + $query = $this->viewQuery->create(); + } elseif ('referer' === $type) { + $query = $this->refererQuery->create(); + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + if (!$this->reload && isset($this->cache[$type])) { + return $this->cache[$type]; + } + + if (null !== $this->from) { + $query + ->andWhere('.date >= :from') + ->setParameter(':from', $this->from) + ; + } + + if (null !== $this->to) { + $query + ->andWhere('.date <= :to') + ->setParameter(':to', $this->to) + ; + } + + if (null !== $this->node) { + $query + ->andWhere('.node = :node') + ->setParameter(':node', $this->node->getId()) + ; + } + + $this->cache[$type] = $query->orderBy('.date')->find(); + + return $this->cache[$type]; + } } diff --git a/core/Controller/Analytic/AnalyticController.php b/core/Controller/Analytic/AnalyticController.php index 83b9640..8a0156b 100644 --- a/core/Controller/Analytic/AnalyticController.php +++ b/core/Controller/Analytic/AnalyticController.php @@ -2,11 +2,11 @@ namespace App\Core\Controller\Analytic; +use App\Core\Analytic\RangeAnalytic; +use App\Core\Entity\Site\Node; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; -use App\Core\Entity\Site\Node; -use App\Core\Analytic\RangeAnalytic; /** * @Route("/admin/analytic") @@ -22,15 +22,16 @@ class AnalyticController extends AbstractController throw $this->createNotFoundException(); } - $views = $rangeAnalytic->getViews(new \DateTime('now - '.$range), new \DateTime(), $node); - $pathViews = $rangeAnalytic->getPathViews(new \DateTime('now - '.$range), new \DateTime(), $node); - $referers = $rangeAnalytic->getReferers(new \DateTime('now - '.$range), new \DateTime(), $node); + $rangeAnalytic + ->setDateRange(new \DateTime('now - '.$range), new \DateTime()) + ->setNode($node) + ; return $this->render('@Core/analytic/stats.html.twig', [ 'range' => $range, - 'views' => $views, - 'pathViews' => $pathViews, - 'referers' => $referers, + 'views' => $rangeAnalytic->getViews(), + 'pathViews' => $rangeAnalytic->getPathViews(), + 'referers' => $rangeAnalytic->getReferers(), 'node' => $node, ]); } From 5810e09a4516b2126eed3a1247d2d9a81fe9af51 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Mon, 21 Feb 2022 16:12:05 +0100 Subject: [PATCH 18/18] add sort of datas by view in DateRangeAnalytic --- ...angeAnalytic.php => DateRangeAnalytic.php} | 20 +++++++++++++++++-- .../Analytic/AnalyticController.php | 12 +++++------ core/EventListener/AnalyticListener.php | 12 ++++++++++- 3 files changed, 35 insertions(+), 9 deletions(-) rename core/Analytic/{RangeAnalytic.php => DateRangeAnalytic.php} (91%) diff --git a/core/Analytic/RangeAnalytic.php b/core/Analytic/DateRangeAnalytic.php similarity index 91% rename from core/Analytic/RangeAnalytic.php rename to core/Analytic/DateRangeAnalytic.php index c4a0f92..ce4a60f 100644 --- a/core/Analytic/RangeAnalytic.php +++ b/core/Analytic/DateRangeAnalytic.php @@ -7,11 +7,11 @@ use App\Core\Repository\Analytic\RefererRepositoryQuery; use App\Core\Repository\Analytic\ViewRepositoryQuery; /** - * class RangeAnalytic. + * class DateRangeAnalytic. * * @author Simon Vieille */ -class RangeAnalytic +class DateRangeAnalytic { protected ViewRepositoryQuery $viewQuery; protected RefererRepositoryQuery $refererQuery; @@ -57,6 +57,8 @@ class RangeAnalytic $datas[$index] += $entity->getViews(); } + arsort($datas, SORT_NUMERIC); + return $datas; } @@ -77,6 +79,8 @@ class RangeAnalytic $datas[$index] += $entity->getViews(); } + arsort($datas, SORT_NUMERIC); + return $datas; } @@ -108,6 +112,18 @@ class RangeAnalytic $datas[$index]['uris'][$path] += $entity->getViews(); } + uasort($datas, function($a, $b) { + if ($a['views'] > $b['views']) { + return -1; + } + + if ($a['views'] < $b['views']) { + return 1; + } + + return 0; + }); + return $datas; } diff --git a/core/Controller/Analytic/AnalyticController.php b/core/Controller/Analytic/AnalyticController.php index 8a0156b..6d2fd88 100644 --- a/core/Controller/Analytic/AnalyticController.php +++ b/core/Controller/Analytic/AnalyticController.php @@ -2,7 +2,7 @@ namespace App\Core\Controller\Analytic; -use App\Core\Analytic\RangeAnalytic; +use App\Core\Analytic\DateRangeAnalytic; use App\Core\Entity\Site\Node; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -16,22 +16,22 @@ class AnalyticController extends AbstractController /** * @Route("/stats/{node}/{range}", name="admin_analytic_stats") */ - public function stats(Node $node, RangeAnalytic $rangeAnalytic, string $range = '7days'): Response + public function stats(Node $node, DateRangeAnalytic $analytic, string $range = '7days'): Response { if (!in_array($range, ['7days', '30days', '90days', '1year'])) { throw $this->createNotFoundException(); } - $rangeAnalytic + $analytic ->setDateRange(new \DateTime('now - '.$range), new \DateTime()) ->setNode($node) ; return $this->render('@Core/analytic/stats.html.twig', [ 'range' => $range, - 'views' => $rangeAnalytic->getViews(), - 'pathViews' => $rangeAnalytic->getPathViews(), - 'referers' => $rangeAnalytic->getReferers(), + 'views' => $analytic->getViews(), + 'pathViews' => $analytic->getPathViews(), + 'referers' => $analytic->getReferers(), 'node' => $node, ]); } diff --git a/core/EventListener/AnalyticListener.php b/core/EventListener/AnalyticListener.php index 6154378..515e39d 100644 --- a/core/EventListener/AnalyticListener.php +++ b/core/EventListener/AnalyticListener.php @@ -99,6 +99,16 @@ class AnalyticListener return; } + $referer = $this->request->headers->get('referer'); + + if (!filter_var($referer, FILTER_VALIDATE_URL) || parse_url($url, PHP_URL_SCHEME)) { + return; + } + + if (!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https'])) { + return; + } + $entity = $this->refererRepositoryQuery->create() ->filterByRequest($this->request) ->andWhere('.date=CURRENT_DATE()') @@ -106,7 +116,7 @@ class AnalyticListener ; if (!$entity) { - $entity = $this->refererFactory->create($this->node, $this->request->headers->get('referer')); + $entity = $this->refererFactory->create($this->node, $referer); } $entity->addView();