Created services and command to process visits

This commit is contained in:
Alejandro Celaya
2016-07-20 19:00:23 +02:00
parent d3c2f4ed2a
commit dbe1281d2a
10 changed files with 190 additions and 46 deletions
+1
View File
@@ -9,6 +9,7 @@ return [
Command\ResolveUrlCommand::class, Command\ResolveUrlCommand::class,
Command\ListShortcodesCommand::class, Command\ListShortcodesCommand::class,
Command\GetVisitsCommand::class, Command\GetVisitsCommand::class,
Command\ProcessVisitsCommand::class,
] ]
], ],
+1
View File
@@ -13,6 +13,7 @@ return [
CLI\Command\ResolveUrlCommand::class => AnnotatedFactory::class, CLI\Command\ResolveUrlCommand::class => AnnotatedFactory::class,
CLI\Command\ListShortcodesCommand::class => AnnotatedFactory::class, CLI\Command\ListShortcodesCommand::class => AnnotatedFactory::class,
CLI\Command\GetVisitsCommand::class => AnnotatedFactory::class, CLI\Command\GetVisitsCommand::class => AnnotatedFactory::class,
CLI\Command\ProcessVisitsCommand::class => AnnotatedFactory::class,
], ],
], ],
@@ -0,0 +1,74 @@
<?php
namespace Shlinkio\Shlink\CLI\Command;
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\Service\IpLocationResolver;
use Shlinkio\Shlink\Common\Service\IpLocationResolverInterface;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Service\VisitService;
use Shlinkio\Shlink\Core\Service\VisitServiceInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ProcessVisitsCommand extends Command
{
const LOCALHOST = '127.0.0.1';
/**
* @var VisitServiceInterface
*/
private $visitService;
/**
* @var IpLocationResolverInterface
*/
private $ipLocationResolver;
/**
* ProcessVisitsCommand constructor.
* @param VisitServiceInterface|VisitService $visitService
* @param IpLocationResolverInterface|IpLocationResolver $ipLocationResolver
*
* @Inject({VisitService::class, IpLocationResolver::class})
*/
public function __construct(VisitServiceInterface $visitService, IpLocationResolverInterface $ipLocationResolver)
{
parent::__construct(null);
$this->visitService = $visitService;
$this->ipLocationResolver = $ipLocationResolver;
}
public function configure()
{
$this->setName('visit:process')
->setDescription('Processes visits where location is not set already');
}
public function execute(InputInterface $input, OutputInterface $output)
{
$visits = $this->visitService->getUnlocatedVisits();
foreach ($visits as $visit) {
$ipAddr = $visit->getRemoteAddr();
$output->write(sprintf('Processing IP <info>%s</info>', $ipAddr));
if ($ipAddr === self::LOCALHOST) {
$output->writeln(' (<comment>Ignored localhost address</comment>)');
continue;
}
try {
$result = $this->ipLocationResolver->resolveIpLocation($ipAddr);
$location = new VisitLocation();
$location->exchangeArray($result);
$visit->setVisitLocation($location);
$this->visitService->saveVisit($visit);
$output->writeln(sprintf(' (Address located at "%s")', $location->getCityName()));
} catch (WrongIpException $e) {
continue;
}
}
$output->writeln('Finished processing all IPs');
}
}
+1
View File
@@ -11,6 +11,7 @@ return [
Service\UrlShortener::class => AnnotatedFactory::class, Service\UrlShortener::class => AnnotatedFactory::class,
Service\VisitsTracker::class => AnnotatedFactory::class, Service\VisitsTracker::class => AnnotatedFactory::class,
Service\ShortUrlService::class => AnnotatedFactory::class, Service\ShortUrlService::class => AnnotatedFactory::class,
Service\VisitService::class => AnnotatedFactory::class,
// Middleware // Middleware
RedirectMiddleware::class => AnnotatedFactory::class, RedirectMiddleware::class => AnnotatedFactory::class,
+2 -2
View File
@@ -9,7 +9,7 @@ use Shlinkio\Shlink\Common\Entity\AbstractEntity;
* @author * @author
* @link * @link
* *
* @ORM\Entity * @ORM\Entity(repositoryClass="Shlinkio\Shlink\Core\Repository\VisitRepository")
* @ORM\Table(name="visits") * @ORM\Table(name="visits")
*/ */
class Visit extends AbstractEntity implements \JsonSerializable class Visit extends AbstractEntity implements \JsonSerializable
@@ -42,7 +42,7 @@ class Visit extends AbstractEntity implements \JsonSerializable
protected $shortUrl; protected $shortUrl;
/** /**
* @var VisitLocation * @var VisitLocation
* @ORM\ManyToOne(targetEntity=VisitLocation::class) * @ORM\ManyToOne(targetEntity=VisitLocation::class, cascade={"persist"})
* @ORM\JoinColumn(name="visit_location_id", referencedColumnName="id", nullable=true) * @ORM\JoinColumn(name="visit_location_id", referencedColumnName="id", nullable=true)
*/ */
protected $visitLocation; protected $visitLocation;
+17 -44
View File
@@ -17,42 +17,37 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
{ {
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $countryCode; protected $countryCode;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $countryName; protected $countryName;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $regionName; protected $regionName;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $cityName; protected $cityName;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $latitude; protected $latitude;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/ */
protected $longitude; protected $longitude;
/** /**
* @var string * @var string
* @ORM\Column() * @ORM\Column(nullable=true)
*/
protected $areaCode;
/**
* @var string
* @ORM\Column()
*/ */
protected $timezone; protected $timezone;
@@ -164,24 +159,6 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
return $this; return $this;
} }
/**
* @return string
*/
public function getAreaCode()
{
return $this->areaCode;
}
/**
* @param string $areaCode
* @return $this
*/
public function setAreaCode($areaCode)
{
$this->areaCode = $areaCode;
return $this;
}
/** /**
* @return string * @return string
*/ */
@@ -208,17 +185,17 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
*/ */
public function exchangeArray(array $array) public function exchangeArray(array $array)
{ {
if (array_key_exists('countryCode', $array)) { if (array_key_exists('country_code', $array)) {
$this->setCountryCode($array['countryCode']); $this->setCountryCode($array['country_code']);
} }
if (array_key_exists('countryName', $array)) { if (array_key_exists('country_name', $array)) {
$this->setCountryName($array['countryName']); $this->setCountryName($array['country_name']);
} }
if (array_key_exists('regionName', $array)) { if (array_key_exists('region_name', $array)) {
$this->setRegionName($array['regionName']); $this->setRegionName($array['region_name']);
} }
if (array_key_exists('cityName', $array)) { if (array_key_exists('city', $array)) {
$this->setCityName($array['cityName']); $this->setCityName($array['city']);
} }
if (array_key_exists('latitude', $array)) { if (array_key_exists('latitude', $array)) {
$this->setLatitude($array['latitude']); $this->setLatitude($array['latitude']);
@@ -226,11 +203,8 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
if (array_key_exists('longitude', $array)) { if (array_key_exists('longitude', $array)) {
$this->setLongitude($array['longitude']); $this->setLongitude($array['longitude']);
} }
if (array_key_exists('areaCode', $array)) { if (array_key_exists('time_zone', $array)) {
$this->setAreaCode($array['areaCode']); $this->setTimezone($array['time_zone']);
}
if (array_key_exists('timezone', $array)) {
$this->setTimezone($array['timezone']);
} }
} }
@@ -248,7 +222,6 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
'cityName' => $this->cityName, 'cityName' => $this->cityName,
'latitude' => $this->latitude, 'latitude' => $this->latitude,
'longitude' => $this->longitude, 'longitude' => $this->longitude,
'areaCode' => $this->areaCode,
'timezone' => $this->timezone, 'timezone' => $this->timezone,
]; ];
} }
@@ -0,0 +1,19 @@
<?php
namespace Shlinkio\Shlink\Core\Repository;
use Doctrine\ORM\EntityRepository;
use Shlinkio\Shlink\Core\Entity\Visit;
class VisitRepository extends EntityRepository implements VisitRepositoryInterface
{
/**
* @return Visit[]
*/
public function findUnlocatedVisits()
{
$qb = $this->createQueryBuilder('v');
$qb->where($qb->expr()->isNull('v.visitLocation'));
return $qb->getQuery()->getResult();
}
}
@@ -0,0 +1,13 @@
<?php
namespace Shlinkio\Shlink\Core\Repository;
use Doctrine\Common\Persistence\ObjectRepository;
use Shlinkio\Shlink\Core\Entity\Visit;
interface VisitRepositoryInterface extends ObjectRepository
{
/**
* @return Visit[]
*/
public function findUnlocatedVisits();
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Shlinkio\Shlink\Core\Service;
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
class VisitService implements VisitServiceInterface
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* VisitService constructor.
* @param EntityManagerInterface $em
*
* @Inject({"em"})
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* @return Visit[]
*/
public function getUnlocatedVisits()
{
/** @var VisitRepository $repo */
$repo = $this->em->getRepository(Visit::class);
return $repo->findUnlocatedVisits();
}
/**
* @param Visit $visit
*/
public function saveVisit(Visit $visit)
{
$this->em->persist($visit);
$this->em->flush();
}
}
@@ -0,0 +1,17 @@
<?php
namespace Shlinkio\Shlink\Core\Service;
use Shlinkio\Shlink\Core\Entity\Visit;
interface VisitServiceInterface
{
/**
* @return Visit[]
*/
public function getUnlocatedVisits();
/**
* @param Visit $visit
*/
public function saveVisit(Visit $visit);
}