diff --git a/.php-version b/.php-version new file mode 100644 index 0000000..cc40bca --- /dev/null +++ b/.php-version @@ -0,0 +1 @@ +8.0 diff --git a/UPGRADE.md b/UPGRADE.md index c8fb1a0..6c79947 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,13 @@ ## [Unreleased] +### Commands + +``` +composer remove jaybizzle/crawler-detect +composer require matomo/device-detector +make doctrine-migration +``` + ## Upgrade to v1.4.0 ### Commands diff --git a/composer.json b/composer.json index fee47b3..1172189 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "knplabs/doctrine-behaviors": "^2.6", "knplabs/knp-paginator-bundle": "^5.8", "liip/imagine-bundle": "^2.7", + "matomo/device-detector": "^5.0", "phpdocumentor/reflection-docblock": "^5.3", "scheb/2fa-google-authenticator": "^5.13", "scheb/2fa-qr-code": "^5.13", diff --git a/core/Analytic/DateRangeAnalytic.php b/core/Analytic/DateRangeAnalytic.php index 36e818d..15f46ae 100644 --- a/core/Analytic/DateRangeAnalytic.php +++ b/core/Analytic/DateRangeAnalytic.php @@ -57,8 +57,6 @@ class DateRangeAnalytic $datas[$index] += $entity->getViews(); } - arsort($datas, SORT_NUMERIC); - return $datas; } @@ -73,13 +71,29 @@ class DateRangeAnalytic $index = $entity->getPath(); if (!isset($datas[$index])) { - $datas[$index] = 0; + $datas[$index] = [ + 'views' => 0, + 'desktopViews' => 0, + 'mobileViews' => 0, + ]; } - $datas[$index] += $entity->getViews(); + $datas[$index]['views'] += $entity->getViews(); + $datas[$index]['desktopViews'] += $entity->getDesktopViews(); + $datas[$index]['mobileViews'] += $entity->getMobileViews(); } - arsort($datas, SORT_NUMERIC); + 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/Entity/Analytic/Referer.php b/core/Entity/Analytic/Referer.php index 1c8b94d..0eeb73c 100644 --- a/core/Entity/Analytic/Referer.php +++ b/core/Entity/Analytic/Referer.php @@ -21,7 +21,7 @@ class Referer implements EntityInterface protected $id; /** - * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews") + * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticReferers") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ protected $node; diff --git a/core/Entity/Analytic/View.php b/core/Entity/Analytic/View.php index 0bd3338..25dd681 100644 --- a/core/Entity/Analytic/View.php +++ b/core/Entity/Analytic/View.php @@ -21,7 +21,7 @@ class View implements EntityInterface protected $id; /** - * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="nodeViews") + * @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticViews") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ protected $node; @@ -36,6 +36,16 @@ class View implements EntityInterface */ protected $views = 0; + /** + * @ORM\Column(type="integer", options={"default"=0}) + */ + protected $desktopViews = 0; + + /** + * @ORM\Column(type="integer", options={"default"=0}) + */ + protected $mobileViews = 0; + /** * @ORM\Column(type="date") */ @@ -89,6 +99,44 @@ class View implements EntityInterface return $this; } + public function getDesktopViews(): ?int + { + return $this->desktopViews; + } + + public function setDesktopViews(int $desktopViews): self + { + $this->desktopViews = $desktopViews; + + return $this; + } + + public function addDesktopView(): self + { + ++$this->desktopViews; + + return $this; + } + + public function getMobileViews(): ?int + { + return $this->mobileViews; + } + + public function setMobileViews(int $mobileViews): self + { + $this->mobileViews = $mobileViews; + + return $this; + } + + public function addMobileView(): self + { + ++$this->mobileViews; + + return $this; + } + public function getDate(): ?\DateTimeInterface { return $this->date; diff --git a/core/EventListener/AnalyticListener.php b/core/EventListener/AnalyticListener.php index fdec2f5..5fe1069 100644 --- a/core/EventListener/AnalyticListener.php +++ b/core/EventListener/AnalyticListener.php @@ -2,18 +2,19 @@ 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; +use App\Core\Entity\Site\Node; +use App\Core\Factory\Analytic\RefererFactory; +use App\Core\Factory\Analytic\ViewFactory; +use App\Core\Manager\EntityManager; +use App\Core\Repository\Analytic\RefererRepositoryQuery; +use App\Core\Repository\Analytic\ViewRepositoryQuery; +use App\Core\Repository\Site\NodeRepository; +use DeviceDetector\Cache\PSR6Bridge; +use DeviceDetector\DeviceDetector; +use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\RequestEvent; /** * class AnalyticListener. @@ -28,7 +29,7 @@ class AnalyticListener protected RefererRepositoryQuery $refererRepositoryQuery; protected RefererFactory $refererFactory; protected EntityManager $manager; - protected CrawlerDetect $crawlerDetect; + protected DeviceDetector $deviceDetector; protected Request $request; protected Node $node; @@ -46,7 +47,7 @@ class AnalyticListener $this->refererRepositoryQuery = $refererRepositoryQuery; $this->refererFactory = $refererFactory; $this->manager = $manager; - $this->crawlerDetect = new CrawlerDetect(); + $this->createDeviceDetector(); } public function onKernelRequest(RequestEvent $event) @@ -57,7 +58,10 @@ class AnalyticListener return; } - if ($this->crawlerDetect->isCrawler($request->headers->get('user-agent'))) { + $this->deviceDetector->setUserAgent($request->headers->get('user-agent')); + $this->deviceDetector->parse(); + + if ($this->deviceDetector->isBot()) { return; } @@ -77,6 +81,14 @@ class AnalyticListener $this->createReferer(); } + protected function createDeviceDetector() + { + $cache = new ApcuAdapter(); + + $this->deviceDetector = new DeviceDetector(); + $this->deviceDetector->setCache(new PSR6Bridge($cache)); + } + protected function createView() { $entity = $this->viewRepositoryQuery->create() @@ -90,6 +102,13 @@ class AnalyticListener } $entity->addView(); + + if ($this->deviceDetector->isDesktop()) { + $entity->addDesktopView(); + } elseif ($this->deviceDetector->isMobile()) { + $entity->addMobileView(); + } + $this->save($entity); } diff --git a/core/Resources/views/analytic/stats.html.twig b/core/Resources/views/analytic/stats.html.twig index 8190fa1..1d7e61d 100644 --- a/core/Resources/views/analytic/stats.html.twig +++ b/core/Resources/views/analytic/stats.html.twig @@ -47,18 +47,22 @@ {{ 'Path'|trans }} - {{ 'Views'|trans }} + {{ 'Views'|trans }} + + {% for path, views in pathViews %} {{ path }} - {{ views }} + {{ views.views }} + {{ views.desktopViews }} + {{ views.mobileViews }} {% else %} - +
diff --git a/symfony.lock b/symfony.lock index fb60411..33f0e16 100644 --- a/symfony.lock +++ b/symfony.lock @@ -160,9 +160,15 @@ "config/routes/liip_imagine.yaml" ] }, + "matomo/device-detector": { + "version": "5.0.4" + }, "monolog/monolog": { "version": "2.2.0" }, + "mustangostang/spyc": { + "version": "0.6.3" + }, "myclabs/php-enum": { "version": "1.8.0" },