Passed
Pull Request — master (#500)
by Alejandro
06:23
created

ShortUrl::matchesCriteria()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 9.2363

Importance

Changes 0
Metric Value
cc 9
eloc 13
c 0
b 0
f 0
nc 5
nop 2
dl 0
loc 22
ccs 12
cts 14
cp 0.8571
crap 9.2363
rs 8.0555
1
<?php
2
declare(strict_types=1);
3
4
namespace Shlinkio\Shlink\Core\Entity;
5
6
use Cake\Chronos\Chronos;
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
10
use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface;
11
use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver;
12
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
13
use Zend\Diactoros\Uri;
14
15
use function array_reduce;
16
use function count;
17
use function Functional\contains;
18
use function Functional\invoke;
19
20
class ShortUrl extends AbstractEntity
21
{
22
    /** @var string */
23
    private $longUrl;
24
    /** @var string */
25
    private $shortCode;
26
    /** @var Chronos */
27
    private $dateCreated;
28
    /** @var Collection|Visit[] */
29
    private $visits;
30
    /** @var Collection|Tag[] */
31
    private $tags;
32
    /** @var Chronos|null */
33
    private $validSince;
34
    /** @var Chronos|null */
35
    private $validUntil;
36
    /** @var integer|null */
37
    private $maxVisits;
38
    /** @var Domain|null */
39
    private $domain;
40
41 46
    public function __construct(
42
        string $longUrl,
43
        ?ShortUrlMeta $meta = null,
44
        ?DomainResolverInterface $domainResolver = null
45
    ) {
46 46
        $meta = $meta ?? ShortUrlMeta::createEmpty();
47
48 46
        $this->longUrl = $longUrl;
49 46
        $this->dateCreated = Chronos::now();
50 46
        $this->visits = new ArrayCollection();
51 46
        $this->tags = new ArrayCollection();
52 46
        $this->validSince = $meta->getValidSince();
53 46
        $this->validUntil = $meta->getValidUntil();
54 46
        $this->maxVisits = $meta->getMaxVisits();
55 46
        $this->shortCode = $meta->getCustomSlug() ?? ''; // TODO logic to calculate short code should be passed somehow
56 46
        $this->domain = $this->domainToEntity($meta->getDomain(), $domainResolver ?? new SimpleDomainResolver());
57
    }
58
59 46
    private function domainToEntity(?string $domain, DomainResolverInterface $domainResolver): ?Domain
60
    {
61 46
        return $domainResolver->resolveDomain($domain);
62
    }
63
64 17
    public function getLongUrl(): string
65
    {
66 17
        return $this->longUrl;
67
    }
68
69 12
    public function getShortCode(): string
70
    {
71 12
        return $this->shortCode;
72
    }
73
74
    // TODO Short code is currently calculated based on the ID, so a setter is needed
75 10
    public function setShortCode(string $shortCode): self
76
    {
77 10
        $this->shortCode = $shortCode;
78 10
        return $this;
79
    }
80
81 10
    public function getDateCreated(): Chronos
82
    {
83 10
        return $this->dateCreated;
84
    }
85
86
    /**
87
     * @return Collection|Tag[]
88
     */
89 19
    public function getTags(): Collection
90
    {
91 19
        return $this->tags;
92
    }
93
94
    /**
95
     * @param Collection|Tag[] $tags
96
     */
97 3
    public function setTags(Collection $tags): self
98
    {
99 3
        $this->tags = $tags;
100 3
        return $this;
101
    }
102
103 1
    public function updateMeta(ShortUrlMeta $shortCodeMeta): void
104
    {
105 1
        if ($shortCodeMeta->hasValidSince()) {
106 1
            $this->validSince = $shortCodeMeta->getValidSince();
107
        }
108 1
        if ($shortCodeMeta->hasValidUntil()) {
109 1
            $this->validUntil = $shortCodeMeta->getValidUntil();
110
        }
111 1
        if ($shortCodeMeta->hasMaxVisits()) {
112 1
            $this->maxVisits = $shortCodeMeta->getMaxVisits();
113
        }
114
    }
115
116 11
    public function getValidSince(): ?Chronos
117
    {
118 11
        return $this->validSince;
119
    }
120
121 11
    public function getValidUntil(): ?Chronos
122
    {
123 11
        return $this->validUntil;
124
    }
125
126 12
    public function getVisitsCount(): int
127
    {
128 12
        return count($this->visits);
129
    }
130
131
    /**
132
     * @param Collection|Visit[] $visits
133
     * @return ShortUrl
134
     * @internal
135
     */
136 4
    public function setVisits(Collection $visits): self
137
    {
138 4
        $this->visits = $visits;
139 4
        return $this;
140
    }
141
142 11
    public function getMaxVisits(): ?int
143
    {
144 11
        return $this->maxVisits;
145
    }
146
147
    public function maxVisitsReached(): bool
148
    {
149
        return $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;
150
    }
151
152 12
    public function toString(array $domainConfig): string
153
    {
154 12
        return (string) (new Uri())->withPath($this->shortCode)
155 12
                                   ->withScheme($domainConfig['schema'] ?? 'http')
156 12
                                   ->withHost($this->resolveDomain($domainConfig['hostname'] ?? ''));
157
    }
158
159 12
    private function resolveDomain(string $fallback = ''): string
160
    {
161 12
        if ($this->domain === null) {
162 12
            return $fallback;
163
        }
164
165
        return $this->domain->getAuthority();
166
    }
167
168 8
    public function matchesCriteria(ShortUrlMeta $meta, array $tags): bool
169
    {
170 8
        if ($meta->hasMaxVisits() && $meta->getMaxVisits() !== $this->maxVisits) {
171 1
            return false;
172
        }
173 8
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq($this->validSince)) {
0 ignored issues
show
Bug introduced by
It seems like $this->validSince can also be of type null; however, parameter $dt of Cake\Chronos\Chronos::eq() does only seem to accept Cake\Chronos\ChronosInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq(/** @scrutinizer ignore-type */ $this->validSince)) {
Loading history...
174
            return false;
175
        }
176 8
        if ($meta->hasValidUntil() && ! $meta->getValidUntil()->eq($this->validUntil)) {
177
            return false;
178
        }
179
180 8
        $shortUrlTags = invoke($this->getTags(), '__toString');
181 8
        $hasAllTags = count($shortUrlTags) === count($tags) && array_reduce(
182 8
            $tags,
183
            function (bool $hasAllTags, string $tag) use ($shortUrlTags) {
184 3
                return $hasAllTags && contains($shortUrlTags, $tag);
185 8
            },
186 8
            true
187
        );
188
189 8
        return $hasAllTags;
190
    }
191
}
192