Created VisitsTracker service to track visits to shortcodes

This commit is contained in:
Alejandro Celaya 2016-04-30 19:18:42 +02:00
parent b2615d0de6
commit 4ae08c02ec
4 changed files with 127 additions and 71 deletions

View file

@ -2,7 +2,7 @@
use Acelaya\UrlShortener\Factory\CacheFactory;
use Acelaya\UrlShortener\Factory\EntityManagerFactory;
use Acelaya\UrlShortener\Middleware;
use Acelaya\UrlShortener\Service\UrlShortener;
use Acelaya\UrlShortener\Service;
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
use Doctrine\Common\Cache\Cache;
use Doctrine\ORM\EntityManager;
@ -34,7 +34,8 @@ return [
// Services
EntityManager::class => EntityManagerFactory::class,
GuzzleHttp\Client::class => InvokableFactory::class,
UrlShortener::class => AnnotatedFactory::class,
Service\UrlShortener::class => AnnotatedFactory::class,
Service\VisitsTracker::class => AnnotatedFactory::class,
Cache::class => CacheFactory::class,
// Middleware

View file

@ -15,7 +15,7 @@ class Visit extends AbstractEntity
{
/**
* @var string
* @ORM\Column(type="string", length=256)
* @ORM\Column(type="string", length=256, nullable=true)
*/
protected $referer;
/**
@ -25,19 +25,14 @@ class Visit extends AbstractEntity
protected $date;
/**
* @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
* @ORM\Column(type="string", length=256)
* @ORM\Column(type="string", length=256, name="user_agent", nullable=true)
*/
protected $platform;
/**
* @var string
* @ORM\Column(type="string", length=256)
*/
protected $browser;
protected $userAgent;
/**
* @var ShortUrl
* @ORM\ManyToOne(targetEntity=ShortUrl::class)
@ -45,6 +40,11 @@ class Visit extends AbstractEntity
*/
protected $shortUrl;
public function __construct()
{
$this->date = new \DateTime();
}
/**
* @return string
*/
@ -81,60 +81,6 @@ class Visit extends AbstractEntity
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
*/
@ -152,4 +98,40 @@ class Visit extends AbstractEntity
$this->shortUrl = $shortUrl;
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;
}
}

View file

@ -3,6 +3,8 @@ namespace Acelaya\UrlShortener\Middleware\Routable;
use Acelaya\UrlShortener\Service\UrlShortener;
use Acelaya\UrlShortener\Service\UrlShortenerInterface;
use Acelaya\UrlShortener\Service\VisitsTracker;
use Acelaya\UrlShortener\Service\VisitsTrackerInterface;
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
@ -15,16 +17,22 @@ class RedirectMiddleware implements MiddlewareInterface
* @var UrlShortenerInterface
*/
private $urlShortener;
/**
* @var VisitsTracker|VisitsTrackerInterface
*/
private $visitTracker;
/**
* RedirectMiddleware constructor.
* @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->visitTracker = $visitTracker;
}
/**
@ -55,7 +63,7 @@ class RedirectMiddleware implements MiddlewareInterface
public function __invoke(Request $request, Response $response, callable $out = null)
{
$shortCode = $request->getAttribute('shortCode', '');
try {
$longUrl = $this->urlShortener->shortCodeToUrl($shortCode);
@ -65,8 +73,12 @@ class RedirectMiddleware implements MiddlewareInterface
return $out($request, $response);
}
// Return a redirect response to the long URL
return new RedirectResponse($longUrl, 301);
// Track visit to this shortcode
$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) {
// In case of error, dispatch 404 error
return $out($request, $response);

View 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;
}
}