VisitLocator   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Test Coverage

Coverage 97.44%

Importance

Changes 0
Metric Value
wmc 11
eloc 32
dl 0
loc 79
ccs 38
cts 39
cp 0.9744
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A locateVisitsWithEmptyLocation() 0 3 1
A locateUnlocatedVisits() 0 3 1
A locateAllVisits() 0 3 1
A __construct() 0 7 1
A locateVisits() 0 32 5
A locateVisit() 0 13 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\Visit;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Shlinkio\Shlink\Core\Entity\Visit;
9
use Shlinkio\Shlink\Core\Entity\VisitLocation;
10
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
11
use Shlinkio\Shlink\Core\Repository\VisitRepositoryInterface;
12
use Shlinkio\Shlink\IpGeolocation\Model\Location;
13
14
class VisitLocator implements VisitLocatorInterface
15
{
16
    private EntityManagerInterface $em;
17
    private VisitRepositoryInterface $repo;
18
19 9
    public function __construct(EntityManagerInterface $em)
20
    {
21 9
        $this->em = $em;
22
23
        /** @var VisitRepositoryInterface $repo */
24 9
        $repo = $em->getRepository(Visit::class);
25 9
        $this->repo = $repo;
26 9
    }
27
28 3
    public function locateUnlocatedVisits(VisitGeolocationHelperInterface $helper): void
29
    {
30 3
        $this->locateVisits($this->repo->findUnlocatedVisits(), $helper);
31 3
    }
32
33 3
    public function locateVisitsWithEmptyLocation(VisitGeolocationHelperInterface $helper): void
34
    {
35 3
        $this->locateVisits($this->repo->findVisitsWithEmptyLocation(), $helper);
36 3
    }
37
38 3
    public function locateAllVisits(VisitGeolocationHelperInterface $helper): void
39
    {
40 3
        $this->locateVisits($this->repo->findAllVisits(), $helper);
41 3
    }
42
43
    /**
44
     * @param iterable|Visit[] $results
45
     */
46 9
    private function locateVisits(iterable $results, VisitGeolocationHelperInterface $helper): void
47
    {
48 9
        $count = 0;
49 9
        $persistBlock = 200;
50
51 9
        foreach ($results as $visit) {
52 9
            $count++;
53
54
            try {
55 9
                $location = $helper->geolocateVisit($visit);
56 6
            } catch (IpCannotBeLocatedException $e) {
57 6
                if (! $e->isNonLocatableAddress()) {
58
                    // Skip if the visit's IP could not be located because of an error
59 3
                    continue;
60
                }
61
62
                // If the IP address is non-locatable, locate it as empty to prevent next processes to pick it again
63 3
                $location = Location::emptyInstance();
64
            }
65
66 6
            $location = new VisitLocation($location);
67 6
            $this->locateVisit($visit, $location, $helper);
68
69
            // Flush and clear after X iterations
70 6
            if ($count % $persistBlock === 0) {
71 3
                $this->em->flush();
72 3
                $this->em->clear();
73
            }
74
        }
75
76 9
        $this->em->flush();
77 9
        $this->em->clear();
78 9
    }
79
80 6
    private function locateVisit(Visit $visit, VisitLocation $location, VisitGeolocationHelperInterface $helper): void
81
    {
82 6
        $prevLocation = $visit->getVisitLocation();
83
84 6
        $visit->locate($location);
85 6
        $this->em->persist($visit);
86
87
        // In order to avoid leaving orphan locations, remove the previous one
88 6
        if ($prevLocation !== null) {
89
            $this->em->remove($prevLocation);
90
        }
91
92 6
        $helper->onVisitLocated($location, $visit);
93 6
    }
94
}
95