diff --git a/module/CLI/src/Command/ShortUrl/GetVisitsCommand.php b/module/CLI/src/Command/ShortUrl/GetVisitsCommand.php index 40bf0986..caf9c4b7 100644 --- a/module/CLI/src/Command/ShortUrl/GetVisitsCommand.php +++ b/module/CLI/src/Command/ShortUrl/GetVisitsCommand.php @@ -5,6 +5,7 @@ namespace Shlinkio\Shlink\CLI\Command\ShortUrl; use Cake\Chronos\Chronos; use Shlinkio\Shlink\Common\Util\DateRange; +use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -13,7 +14,8 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Zend\I18n\Translator\TranslatorInterface; -use function array_values; +use function array_map; +use function Shlinkio\Shlink\Common\pick; class GetVisitsCommand extends Command { @@ -87,17 +89,11 @@ class GetVisitsCommand extends Command $endDate = $this->getDateOption($input, 'endDate'); $visits = $this->visitsTracker->info($shortCode, new DateRange($startDate, $endDate)); - $rows = []; - foreach ($visits as $row) { - $rowData = $row->jsonSerialize(); - - // Unset location info and remote addr - unset($rowData['visitLocation'], $rowData['remoteAddr']); - - $rowData['country'] = $row->getVisitLocation()->getCountryName(); - - $rows[] = array_values($rowData); - } + $rows = array_map(function (Visit $visit) { + $rowData = $visit->jsonSerialize(); + $rowData['country'] = $visit->getVisitLocation()->getCountryName(); + return pick($rowData, ['referer', 'date', 'userAgent', 'country']); + }, $visits); $io->table([ $this->translator->translate('Referer'), $this->translator->translate('Date'), diff --git a/module/CLI/src/Command/Visit/ProcessVisitsCommand.php b/module/CLI/src/Command/Visit/ProcessVisitsCommand.php index 871e513d..f1f70e35 100644 --- a/module/CLI/src/Command/Visit/ProcessVisitsCommand.php +++ b/module/CLI/src/Command/Visit/ProcessVisitsCommand.php @@ -80,8 +80,7 @@ class ProcessVisitsCommand extends Command try { $result = $this->ipLocationResolver->resolveIpLocation($ipAddr); - $location = new VisitLocation(); - $location->exchangeArray($result); + $location = new VisitLocation($result); $visit->setVisitLocation($location); $this->visitService->saveVisit($visit); diff --git a/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php b/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php index 88563a05..6a3322b7 100644 --- a/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php @@ -80,7 +80,7 @@ class GetVisitsCommandTest extends TestCase $shortCode = 'abc123'; $this->visitsTracker->info($shortCode, Argument::any())->willReturn([ (new Visit())->setReferer('foo') - ->setVisitLocation((new VisitLocation())->setCountryName('Spain')) + ->setVisitLocation(new VisitLocation(['country_name' => 'Spain'])) ->setUserAgent('bar'), ])->shouldBeCalledTimes(1); diff --git a/module/Common/functions/functions.php b/module/Common/functions/functions.php index ac0eb94f..7ba4d515 100644 --- a/module/Common/functions/functions.php +++ b/module/Common/functions/functions.php @@ -3,7 +3,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Common; +use const ARRAY_FILTER_USE_KEY; use const JSON_ERROR_NONE; +use function array_filter; use function getenv; use function in_array; use function json_decode as spl_json_decode; @@ -52,6 +54,19 @@ function contains($needle, array $haystack): bool return in_array($needle, $haystack, true); } +/** + * Returns only the keys in keysToPick from provided array + * + * @param array $array + * @param array $keysToPick + */ +function pick(array $array, array $keysToPick): array +{ + return array_filter($array, function (string $key) use ($keysToPick) { + return contains($key, $keysToPick); + }, ARRAY_FILTER_USE_KEY); +} + /** * @throws Exception\InvalidArgumentException */ diff --git a/module/Core/src/Entity/VisitLocation.php b/module/Core/src/Entity/VisitLocation.php index daeb9678..f9ee7250 100644 --- a/module/Core/src/Entity/VisitLocation.php +++ b/module/Core/src/Entity/VisitLocation.php @@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Core\Entity; use Doctrine\ORM\Mapping as ORM; use JsonSerializable; use Shlinkio\Shlink\Common\Entity\AbstractEntity; -use Zend\Stdlib\ArraySerializableInterface; use function array_key_exists; /** @@ -17,7 +16,7 @@ use function array_key_exists; * @ORM\Entity() * @ORM\Table(name="visit_locations") */ -class VisitLocation extends AbstractEntity implements ArraySerializableInterface, JsonSerializable +class VisitLocation extends AbstractEntity implements JsonSerializable { /** * @var string @@ -55,15 +54,9 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface */ private $timezone; - public function getCountryCode(): string + public function __construct(array $locationInfo) { - return $this->countryCode ?? ''; - } - - public function setCountryCode(string $countryCode) - { - $this->countryCode = $countryCode; - return $this; + $this->exchangeArray($locationInfo); } public function getCountryName(): string @@ -71,99 +64,50 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface return $this->countryName ?? ''; } - public function setCountryName(string $countryName): self - { - $this->countryName = $countryName; - return $this; - } - - public function getRegionName(): string - { - return $this->regionName ?? ''; - } - - public function setRegionName(string $regionName): self - { - $this->regionName = $regionName; - return $this; - } - - public function getCityName(): string - { - return $this->cityName ?? ''; - } - - public function setCityName(string $cityName): self - { - $this->cityName = $cityName; - return $this; - } - public function getLatitude(): string { return $this->latitude ?? ''; } - public function setLatitude(string $latitude): self - { - $this->latitude = $latitude; - return $this; - } - public function getLongitude(): string { return $this->longitude ?? ''; } - public function setLongitude(string $longitude): self + public function getCityName(): string { - $this->longitude = $longitude; - return $this; - } - - public function getTimezone(): string - { - return $this->timezone ?? ''; - } - - public function setTimezone(string $timezone): self - { - $this->timezone = $timezone; - return $this; + return $this->cityName ?? ''; } /** * Exchange internal values from provided array */ - public function exchangeArray(array $array): void + private function exchangeArray(array $array): void { if (array_key_exists('country_code', $array)) { - $this->setCountryCode((string) $array['country_code']); + $this->countryCode = (string) $array['country_code']; } if (array_key_exists('country_name', $array)) { - $this->setCountryName((string) $array['country_name']); + $this->countryName = (string) $array['country_name']; } if (array_key_exists('region_name', $array)) { - $this->setRegionName((string) $array['region_name']); + $this->regionName = (string) $array['region_name']; } if (array_key_exists('city', $array)) { - $this->setCityName((string) $array['city']); + $this->cityName = (string) $array['city']; } if (array_key_exists('latitude', $array)) { - $this->setLatitude((string) $array['latitude']); + $this->latitude = (string) $array['latitude']; } if (array_key_exists('longitude', $array)) { - $this->setLongitude((string) $array['longitude']); + $this->longitude = (string) $array['longitude']; } if (array_key_exists('time_zone', $array)) { - $this->setTimezone((string) $array['time_zone']); + $this->timezone = (string) $array['time_zone']; } } - /** - * Return an array representation of the object - */ - public function getArrayCopy(): array + public function jsonSerialize(): array { return [ 'countryCode' => $this->countryCode, @@ -175,9 +119,4 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface 'timezone' => $this->timezone, ]; } - - public function jsonSerialize(): array - { - return $this->getArrayCopy(); - } } diff --git a/module/Core/test-func/Repository/VisitRepositoryTest.php b/module/Core/test-func/Repository/VisitRepositoryTest.php index 1cae6720..74dd9c17 100644 --- a/module/Core/test-func/Repository/VisitRepositoryTest.php +++ b/module/Core/test-func/Repository/VisitRepositoryTest.php @@ -39,7 +39,7 @@ class VisitRepositoryTest extends DatabaseTestCase $visit = new Visit(); if ($i % 2 === 0) { - $location = new VisitLocation(); + $location = new VisitLocation([]); $this->getEntityManager()->persist($location); $visit->setVisitLocation($location); } diff --git a/module/Core/test/Entity/VisitLocationTest.php b/module/Core/test/Entity/VisitLocationTest.php index d6ccac46..17d4045e 100644 --- a/module/Core/test/Entity/VisitLocationTest.php +++ b/module/Core/test/Entity/VisitLocationTest.php @@ -18,8 +18,7 @@ class VisitLocationTest extends TestCase 'longitude' => -2000.4, ]; - $location = new VisitLocation(); - $location->exchangeArray($payload); + $location = new VisitLocation($payload); $this->assertSame('1000.7', $location->getLatitude()); $this->assertSame('-2000.4', $location->getLongitude());