- move NodeView to Analytic/View

- move NodeViewListener to AnalyticListener
- add referer
This commit is contained in:
Simon Vieille 2022-02-20 15:25:07 +01:00
parent 58c8b6126e
commit ec4b3341c8
17 changed files with 393 additions and 116 deletions

2
.gitignore vendored
View file

@ -27,3 +27,5 @@ yarn-error.log
/public/uploads/
!/public/uploads/.gitkeep
/public/media/
/migrations/
!/migrations/.gitkeep

View file

@ -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",

View file

@ -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 }

View file

@ -0,0 +1,103 @@
<?php
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
/**
* @ORM\Entity(repositoryClass=ViewRepository::class)
* @ORM\Table(name="analytic_referer")
*/
class Referer implements EntityInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $node;
/**
* @ORM\Column(type="string", length=255)
*/
private $uri;
/**
* @ORM\Column(type="integer", options={"default"=0})
*/
private $views = 0;
/**
* @ORM\Column(type="date")
*/
private $date;
public function getId(): ?int
{
return $this->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;
}
}

View file

@ -1,16 +1,17 @@
<?php
namespace App\Core\Entity;
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\NodeViewRepository;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
/**
* @ORM\Entity(repositoryClass=NodeViewRepository::class)
* @ORM\Entity(repositoryClass=ViewRepository::class)
* @ORM\Table(name="analytic_view")
*/
class NodeView implements EntityInterface
class View implements EntityInterface
{
/**
* @ORM\Id
@ -21,7 +22,7 @@ class NodeView implements EntityInterface
/**
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews")
* @ORM\JoinColumn(nullable=false)
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $node;

View file

@ -12,6 +12,8 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use function Symfony\Component\String\u;
use App\Core\Entity\Analytic\View;
use App\Core\Entity\Analytic\Referer;
/**
* @Gedmo\Tree(type="nested")
@ -143,18 +145,24 @@ class Node implements EntityInterface
/**
* @ORM\Column(type="boolean", options={"default"=0})
*/
private $enableViewCounter = false;
protected $enableViewCounter = false;
/**
* @ORM\OneToMany(targetEntity=NodeView::class, mappedBy="node", orphanRemoval=true)
* @ORM\OneToMany(targetEntity=View::class, mappedBy="node")
*/
private $nodeViews;
protected $analyticViews;
/**
* @ORM\OneToMany(targetEntity=Referer::class, mappedBy="node")
*/
protected $analyticReferers;
public function __construct()
{
$this->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);
}
}

View file

@ -0,0 +1,124 @@
<?php
namespace App\Core\EventListener;
use App\Core\Manager\EntityManager;
use App\Core\Repository\Site\NodeRepositoryQuery;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use App\Core\Repository\Site\NodeRepository;
use App\Core\Repository\Analytic\ViewRepositoryQuery;
use App\Core\Factory\Analytic\ViewFactory;
use App\Core\Entity\Site\Node;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\Analytic\RefererRepositoryQuery;
use App\Core\Factory\Analytic\RefererFactory;
use App\Core\Entity\EntityInterface;
use Jaybizzle\CrawlerDetect\CrawlerDetect;
/**
* class AnalyticListener.
*
* @author Simon Vieille <simon@deblan.fr>
*/
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);
}
}
}

View file

@ -1,68 +0,0 @@
<?php
namespace App\Core\EventListener;
use App\Core\Factory\NodeViewFactory;
use App\Core\Manager\EntityManager;
use App\Core\Repository\NodeViewRepositoryQuery;
use App\Core\Repository\Site\NodeRepositoryQuery;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use App\Core\Repository\Site\NodeRepository;
/**
* class NodeViewListener.
*
* @author Simon Vieille <simon@deblan.fr>
*/
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);
}
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Core\Factory\Analytic;
use App\Core\Entity\Analytic\Referer as Entity;
use App\Core\Entity\Site\Node;
use App\Core\Factory\FactoryInterface;
class RefererFactory implements FactoryInterface
{
public function create(Node $node, string $uri): Entity
{
$entity = new Entity();
$entity
->setNode($node)
->setUri($uri)
->setDate(new \DateTime())
;
return $entity;
}
}

View file

@ -1,11 +1,12 @@
<?php
namespace App\Core\Factory;
namespace App\Core\Factory\Analytic;
use App\Core\Entity\NodeView as Entity;
use App\Core\Entity\Analytic\View as Entity;
use App\Core\Entity\Site\Node;
use App\Core\Factory\FactoryInterface;
class NodeViewFactory implements FactoryInterface
class ViewFactory implements FactoryInterface
{
public function create(Node $node, string $path): Entity
{

View file

@ -0,0 +1,21 @@
<?php
namespace App\Core\Repository\Analytic;
use App\Core\Entity\Analytic\Referer;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Referer|null find($id, $lockMode = null, $lockVersion = null)
* @method Referer|null findOneBy(array $criteria, array $orderBy = null)
* @method Referer[] findAll()
* @method Referer[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RefererRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Referer::class);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Core\Repository\Analytic;
use App\Core\Repository\Analytic\RefererRepository as Repository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class RefererRepositoryQuery extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, 'n', $paginator);
}
public function filterByRequest(Request $request)
{
return $this
->andWhere('.node = :node')
->andWhere('.uri = :uri')
->setParameters([
':node' => $request->attributes->get('_node'),
':uri' => $request->headers->get('referer'),
])
;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Core\Repository\Analytic;
use App\Core\Entity\Analytic\View;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method View|null find($id, $lockMode = null, $lockVersion = null)
* @method View|null findOneBy(array $criteria, array $orderBy = null)
* @method View[] findAll()
* @method View[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ViewRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, View::class);
}
}

View file

@ -1,12 +1,13 @@
<?php
namespace App\Core\Repository;
namespace App\Core\Repository\Analytic;
use App\Core\Repository\NodeViewRepository as Repository;
use App\Core\Repository\Analytic\ViewRepository as Repository;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class NodeViewRepositoryQuery extends RepositoryQuery
class ViewRepositoryQuery extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{

View file

@ -1,21 +0,0 @@
<?php
namespace App\Core\Repository;
use App\Core\Entity\NodeView;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method NodeView|null find($id, $lockMode = null, $lockVersion = null)
* @method NodeView|null findOneBy(array $criteria, array $orderBy = null)
* @method NodeView[] findAll()
* @method NodeView[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class NodeViewRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, NodeView::class);
}
}

View file

View file

@ -129,6 +129,9 @@
"imagine/imagine": {
"version": "1.2.4"
},
"jaybizzle/crawler-detect": {
"version": "v1.2.110"
},
"khanamiryan/qrcode-detector-decoder": {
"version": "1.0.4"
},