Completed
Push — master ( eb17ea...a61a7d )
by Alejandro
26s queued 11s
created

ShortUrl::matchesCriteria()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 11.7975

Importance

Changes 0
Metric Value
cc 11
eloc 15
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 25
ccs 13
cts 16
cp 0.8125
crap 11.7975
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\Entity;
6
7
use Cake\Chronos\Chronos;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\Common\Collections\Collection;
10
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
11
use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface;
12
use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver;
13
use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException;
14
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
15
use Zend\Diactoros\Uri;
16
17
use function array_reduce;
18
use function count;
19
use function Functional\contains;
20
use function Functional\invoke;
21
use function Shlinkio\Shlink\Core\generateRandomShortCode;
22
23
class ShortUrl extends AbstractEntity
24
{
25
    /** @var string */
26
    private $longUrl;
27
    /** @var string */
28
    private $shortCode;
29
    /** @var Chronos */
30
    private $dateCreated;
31
    /** @var Collection|Visit[] */
32
    private $visits;
33
    /** @var Collection|Tag[] */
34
    private $tags;
35
    /** @var Chronos|null */
36
    private $validSince;
37
    /** @var Chronos|null */
38
    private $validUntil;
39
    /** @var integer|null */
40
    private $maxVisits;
41
    /** @var Domain|null */
42
    private $domain;
43
    /** @var bool */
44
    private $customSlugWasProvided;
45
46 48
    public function __construct(
47
        string $longUrl,
48
        ?ShortUrlMeta $meta = null,
49
        ?DomainResolverInterface $domainResolver = null
50
    ) {
51 48
        $meta = $meta ?? ShortUrlMeta::createEmpty();
52
53 48
        $this->longUrl = $longUrl;
54 48
        $this->dateCreated = Chronos::now();
55 48
        $this->visits = new ArrayCollection();
56 48
        $this->tags = new ArrayCollection();
57 48
        $this->validSince = $meta->getValidSince();
58 48
        $this->validUntil = $meta->getValidUntil();
59 48
        $this->maxVisits = $meta->getMaxVisits();
60 48
        $this->customSlugWasProvided = $meta->hasCustomSlug();
61 48
        $this->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode();
62 48
        $this->domain = ($domainResolver ?? new SimpleDomainResolver())->resolveDomain($meta->getDomain());
63
    }
64
65 19
    public function getLongUrl(): string
66
    {
67 19
        return $this->longUrl;
68
    }
69
70 20
    public function getShortCode(): string
71
    {
72 20
        return $this->shortCode;
73
    }
74
75 10
    public function getDateCreated(): Chronos
76
    {
77 10
        return $this->dateCreated;
78
    }
79
80
    /**
81
     * @return Collection|Tag[]
82
     */
83 20
    public function getTags(): Collection
84
    {
85 20
        return $this->tags;
86
    }
87
88
    /**
89
     * @param Collection|Tag[] $tags
90
     */
91 5
    public function setTags(Collection $tags): self
92
    {
93 5
        $this->tags = $tags;
94 5
        return $this;
95
    }
96
97 1
    public function updateMeta(ShortUrlMeta $shortCodeMeta): void
98
    {
99 1
        if ($shortCodeMeta->hasValidSince()) {
100 1
            $this->validSince = $shortCodeMeta->getValidSince();
101
        }
102 1
        if ($shortCodeMeta->hasValidUntil()) {
103 1
            $this->validUntil = $shortCodeMeta->getValidUntil();
104
        }
105 1
        if ($shortCodeMeta->hasMaxVisits()) {
106 1
            $this->maxVisits = $shortCodeMeta->getMaxVisits();
107
        }
108
    }
109
110
    /**
111
     * @throws ShortCodeCannotBeRegeneratedException
112
     */
113 4
    public function regenerateShortCode(): self
114
    {
115
        // In ShortUrls where a custom slug was provided, do nothing
116 4
        if ($this->customSlugWasProvided) {
117 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlWithCustomSlug();
118
        }
119
120
        // The short code can be regenerated only on ShortUrl which have not been persisted yet
121 3
        if ($this->id !== null) {
122 1
            throw ShortCodeCannotBeRegeneratedException::forShortUrlAlreadyPersisted();
123
        }
124
125 2
        $this->shortCode = generateRandomShortCode();
126 2
        return $this;
127
    }
128
129 11
    public function getValidSince(): ?Chronos
130
    {
131 11
        return $this->validSince;
132
    }
133
134 11
    public function getValidUntil(): ?Chronos
135
    {
136 11
        return $this->validUntil;
137
    }
138
139 12
    public function getVisitsCount(): int
140
    {
141 12
        return count($this->visits);
142
    }
143
144
    /**
145
     * @param Collection|Visit[] $visits
146
     * @return ShortUrl
147
     * @internal
148
     */
149 4
    public function setVisits(Collection $visits): self
150
    {
151 4
        $this->visits = $visits;
152 4
        return $this;
153
    }
154
155 11
    public function getMaxVisits(): ?int
156
    {
157 11
        return $this->maxVisits;
158
    }
159
160
    public function maxVisitsReached(): bool
161
    {
162
        return $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;
163
    }
164
165 12
    public function toString(array $domainConfig): string
166
    {
167 12
        return (string) (new Uri())->withPath($this->shortCode)
168 12
                                   ->withScheme($domainConfig['schema'] ?? 'http')
169 12
                                   ->withHost($this->resolveDomain($domainConfig['hostname'] ?? ''));
170
    }
171
172 13
    private function resolveDomain(string $fallback = ''): string
173
    {
174 13
        if ($this->domain === null) {
175 12
            return $fallback;
176
        }
177
178 1
        return $this->domain->getAuthority();
179
    }
180
181 9
    public function matchesCriteria(ShortUrlMeta $meta, array $tags): bool
182
    {
183 9
        if ($meta->hasMaxVisits() && $meta->getMaxVisits() !== $this->maxVisits) {
184 1
            return false;
185
        }
186 9
        if ($meta->hasDomain() && $meta->getDomain() !== $this->resolveDomain()) {
187
            return false;
188
        }
189 9
        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

189
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq(/** @scrutinizer ignore-type */ $this->validSince)) {
Loading history...
190
            return false;
191
        }
192 9
        if ($meta->hasValidUntil() && ! $meta->getValidUntil()->eq($this->validUntil)) {
193
            return false;
194
        }
195
196 9
        $shortUrlTags = invoke($this->getTags(), '__toString');
197 9
        $hasAllTags = count($shortUrlTags) === count($tags) && array_reduce(
198 9
            $tags,
199
            function (bool $hasAllTags, string $tag) use ($shortUrlTags) {
200 3
                return $hasAllTags && contains($shortUrlTags, $tag);
201 9
            },
202 9
            true
203
        );
204
205 9
        return $hasAllTags;
206
    }
207
}
208