Completed
Pull Request — develop (#578)
by Alejandro
08:17
created

ShortUrl::matchesCriteria()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 11.968

Importance

Changes 0
Metric Value
cc 11
eloc 14
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 23
rs 7.3166
ccs 12
cts 15
cp 0.8
crap 11.968

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

181
        if ($meta->hasValidSince() && ! $meta->getValidSince()->eq(/** @scrutinizer ignore-type */ $this->validSince)) {
Loading history...
182
            return false;
183
        }
184 9
        if ($meta->hasValidUntil() && ! $meta->getValidUntil()->eq($this->validUntil)) {
185
            return false;
186
        }
187
188 9
        $shortUrlTags = invoke($this->getTags(), '__toString');
189 9
        $hasAllTags = count($shortUrlTags) === count($tags) && array_reduce(
190 9
            $tags,
191 9
            fn (bool $hasAllTags, string $tag) => $hasAllTags && contains($shortUrlTags, $tag),
192 9
            true
193
        );
194
195 9
        return $hasAllTags;
196
    }
197
}
198