mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-23 05:13:13 +03:00
Created VisitsTracker service to track visits to shortcodes
This commit is contained in:
parent
b2615d0de6
commit
4ae08c02ec
4 changed files with 127 additions and 71 deletions
|
@ -2,7 +2,7 @@
|
||||||
use Acelaya\UrlShortener\Factory\CacheFactory;
|
use Acelaya\UrlShortener\Factory\CacheFactory;
|
||||||
use Acelaya\UrlShortener\Factory\EntityManagerFactory;
|
use Acelaya\UrlShortener\Factory\EntityManagerFactory;
|
||||||
use Acelaya\UrlShortener\Middleware;
|
use Acelaya\UrlShortener\Middleware;
|
||||||
use Acelaya\UrlShortener\Service\UrlShortener;
|
use Acelaya\UrlShortener\Service;
|
||||||
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
||||||
use Doctrine\Common\Cache\Cache;
|
use Doctrine\Common\Cache\Cache;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
|
@ -34,7 +34,8 @@ return [
|
||||||
// Services
|
// Services
|
||||||
EntityManager::class => EntityManagerFactory::class,
|
EntityManager::class => EntityManagerFactory::class,
|
||||||
GuzzleHttp\Client::class => InvokableFactory::class,
|
GuzzleHttp\Client::class => InvokableFactory::class,
|
||||||
UrlShortener::class => AnnotatedFactory::class,
|
Service\UrlShortener::class => AnnotatedFactory::class,
|
||||||
|
Service\VisitsTracker::class => AnnotatedFactory::class,
|
||||||
Cache::class => CacheFactory::class,
|
Cache::class => CacheFactory::class,
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Visit extends AbstractEntity
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(type="string", length=256)
|
* @ORM\Column(type="string", length=256, nullable=true)
|
||||||
*/
|
*/
|
||||||
protected $referer;
|
protected $referer;
|
||||||
/**
|
/**
|
||||||
|
@ -25,19 +25,14 @@ class Visit extends AbstractEntity
|
||||||
protected $date;
|
protected $date;
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(type="string", length=256)
|
* @ORM\Column(type="string", length=256, name="remote_addr", nullable=true)
|
||||||
*/
|
*/
|
||||||
protected $country;
|
protected $remoteAddr;
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(type="string", length=256)
|
* @ORM\Column(type="string", length=256, name="user_agent", nullable=true)
|
||||||
*/
|
*/
|
||||||
protected $platform;
|
protected $userAgent;
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @ORM\Column(type="string", length=256)
|
|
||||||
*/
|
|
||||||
protected $browser;
|
|
||||||
/**
|
/**
|
||||||
* @var ShortUrl
|
* @var ShortUrl
|
||||||
* @ORM\ManyToOne(targetEntity=ShortUrl::class)
|
* @ORM\ManyToOne(targetEntity=ShortUrl::class)
|
||||||
|
@ -45,6 +40,11 @@ class Visit extends AbstractEntity
|
||||||
*/
|
*/
|
||||||
protected $shortUrl;
|
protected $shortUrl;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->date = new \DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -81,60 +81,6 @@ class Visit extends AbstractEntity
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getCountry()
|
|
||||||
{
|
|
||||||
return $this->country;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $country
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setCountry($country)
|
|
||||||
{
|
|
||||||
$this->country = $country;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getPlatform()
|
|
||||||
{
|
|
||||||
return $this->platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $platform
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setPlatform($platform)
|
|
||||||
{
|
|
||||||
$this->platform = $platform;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getBrowser()
|
|
||||||
{
|
|
||||||
return $this->browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $browser
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setBrowser($browser)
|
|
||||||
{
|
|
||||||
$this->browser = $browser;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ShortUrl
|
* @return ShortUrl
|
||||||
*/
|
*/
|
||||||
|
@ -152,4 +98,40 @@ class Visit extends AbstractEntity
|
||||||
$this->shortUrl = $shortUrl;
|
$this->shortUrl = $shortUrl;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRemoteAddr()
|
||||||
|
{
|
||||||
|
return $this->remoteAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $remoteAddr
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRemoteAddr($remoteAddr)
|
||||||
|
{
|
||||||
|
$this->remoteAddr = $remoteAddr;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUserAgent()
|
||||||
|
{
|
||||||
|
return $this->userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userAgent
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setUserAgent($userAgent)
|
||||||
|
{
|
||||||
|
$this->userAgent = $userAgent;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ namespace Acelaya\UrlShortener\Middleware\Routable;
|
||||||
|
|
||||||
use Acelaya\UrlShortener\Service\UrlShortener;
|
use Acelaya\UrlShortener\Service\UrlShortener;
|
||||||
use Acelaya\UrlShortener\Service\UrlShortenerInterface;
|
use Acelaya\UrlShortener\Service\UrlShortenerInterface;
|
||||||
|
use Acelaya\UrlShortener\Service\VisitsTracker;
|
||||||
|
use Acelaya\UrlShortener\Service\VisitsTrackerInterface;
|
||||||
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
@ -15,16 +17,22 @@ class RedirectMiddleware implements MiddlewareInterface
|
||||||
* @var UrlShortenerInterface
|
* @var UrlShortenerInterface
|
||||||
*/
|
*/
|
||||||
private $urlShortener;
|
private $urlShortener;
|
||||||
|
/**
|
||||||
|
* @var VisitsTracker|VisitsTrackerInterface
|
||||||
|
*/
|
||||||
|
private $visitTracker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RedirectMiddleware constructor.
|
* RedirectMiddleware constructor.
|
||||||
* @param UrlShortenerInterface|UrlShortener $urlShortener
|
* @param UrlShortenerInterface|UrlShortener $urlShortener
|
||||||
|
* @param VisitsTrackerInterface|VisitsTracker $visitTracker
|
||||||
*
|
*
|
||||||
* @Inject({UrlShortener::class})
|
* @Inject({UrlShortener::class, VisitsTracker::class})
|
||||||
*/
|
*/
|
||||||
public function __construct(UrlShortenerInterface $urlShortener)
|
public function __construct(UrlShortenerInterface $urlShortener, VisitsTrackerInterface $visitTracker)
|
||||||
{
|
{
|
||||||
$this->urlShortener = $urlShortener;
|
$this->urlShortener = $urlShortener;
|
||||||
|
$this->visitTracker = $visitTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +63,7 @@ class RedirectMiddleware implements MiddlewareInterface
|
||||||
public function __invoke(Request $request, Response $response, callable $out = null)
|
public function __invoke(Request $request, Response $response, callable $out = null)
|
||||||
{
|
{
|
||||||
$shortCode = $request->getAttribute('shortCode', '');
|
$shortCode = $request->getAttribute('shortCode', '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$longUrl = $this->urlShortener->shortCodeToUrl($shortCode);
|
$longUrl = $this->urlShortener->shortCodeToUrl($shortCode);
|
||||||
|
|
||||||
|
@ -65,8 +73,12 @@ class RedirectMiddleware implements MiddlewareInterface
|
||||||
return $out($request, $response);
|
return $out($request, $response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a redirect response to the long URL
|
// Track visit to this shortcode
|
||||||
return new RedirectResponse($longUrl, 301);
|
$this->visitTracker->track($shortCode);
|
||||||
|
|
||||||
|
// Return a redirect response to the long URL.
|
||||||
|
// Use a temporary redirect to make sure browsers aleways hit the server for analytics purposes
|
||||||
|
return new RedirectResponse($longUrl);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// In case of error, dispatch 404 error
|
// In case of error, dispatch 404 error
|
||||||
return $out($request, $response);
|
return $out($request, $response);
|
||||||
|
|
61
src/Service/VisitsTracker.php
Normal file
61
src/Service/VisitsTracker.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
namespace Acelaya\UrlShortener\Service;
|
||||||
|
|
||||||
|
use Acelaya\UrlShortener\Entity\ShortUrl;
|
||||||
|
use Acelaya\UrlShortener\Entity\Visit;
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
class VisitsTracker implements VisitsTrackerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EntityManagerInterface
|
||||||
|
*/
|
||||||
|
private $em;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VisitsTracker constructor.
|
||||||
|
* @param EntityManagerInterface $em
|
||||||
|
*
|
||||||
|
* @Inject({"em"})
|
||||||
|
*/
|
||||||
|
public function __construct(EntityManagerInterface $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks a new visit to provided short code, using an array of data to look up information
|
||||||
|
*
|
||||||
|
* @param string $shortCode
|
||||||
|
* @param array $visitorData Defaults to global $_SERVER
|
||||||
|
*/
|
||||||
|
public function track($shortCode, array $visitorData = null)
|
||||||
|
{
|
||||||
|
$visitorData = $visitorData ?: $_SERVER;
|
||||||
|
|
||||||
|
/** @var ShortUrl $shortUrl */
|
||||||
|
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([
|
||||||
|
'shortCode' => $shortCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$visit = new Visit();
|
||||||
|
$visit->setShortUrl($shortUrl)
|
||||||
|
->setUserAgent($this->getArrayValue($visitorData, 'HTTP_USER_AGENT'))
|
||||||
|
->setReferer($this->getArrayValue($visitorData, 'REFERER'))
|
||||||
|
->setRemoteAddr($this->getArrayValue($visitorData, 'REMOTE_ADDR'));
|
||||||
|
$this->em->persist($visit);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $array
|
||||||
|
* @param $key
|
||||||
|
* @param null $default
|
||||||
|
* @return mixed|null
|
||||||
|
*/
|
||||||
|
protected function getArrayValue(array $array, $key, $default = null)
|
||||||
|
{
|
||||||
|
return isset($array[$key]) ? $array[$key] : $default;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue