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