Passed
Push — develop ( 0d7fb1...8577d6 )
by Alejandro
09:01 queued 01:01
created

UrlShortener::shorten()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 18
ccs 12
cts 12
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 3
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\Service;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Shlinkio\Shlink\Core\Entity\ShortUrl;
9
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
10
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
11
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
12
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
13
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface;
14
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
15
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
16
use Shlinkio\Shlink\Core\Util\UrlValidatorInterface;
17
use Throwable;
18
19
class UrlShortener implements UrlShortenerInterface
20
{
21
    use TagManagerTrait;
22
23
    private EntityManagerInterface $em;
24
    private UrlValidatorInterface $urlValidator;
25
    private ShortUrlRelationResolverInterface $relationResolver;
26
    private ShortCodeHelperInterface $shortCodeHelper;
27
28 12
    public function __construct(
29
        UrlValidatorInterface $urlValidator,
30
        EntityManagerInterface $em,
31
        ShortUrlRelationResolverInterface $relationResolver,
32
        ShortCodeHelperInterface $shortCodeHelper
33
    ) {
34 12
        $this->urlValidator = $urlValidator;
35 12
        $this->em = $em;
36 12
        $this->relationResolver = $relationResolver;
37 12
        $this->shortCodeHelper = $shortCodeHelper;
38 12
    }
39
40
    /**
41
     * @param string[] $tags
42
     * @throws NonUniqueSlugException
43
     * @throws InvalidUrlException
44
     * @throws Throwable
45
     */
46 12
    public function shorten(string $url, array $tags, ShortUrlMeta $meta): ShortUrl
47
    {
48
        // First, check if a short URL exists for all provided params
49 12
        $existingShortUrl = $this->findExistingShortUrlIfExists($url, $tags, $meta);
50 12
        if ($existingShortUrl !== null) {
51 9
            return $existingShortUrl;
52
        }
53
54 4
        $this->urlValidator->validateUrl($url, $meta->doValidateUrl());
55 4
56 4
        return $this->em->transactional(function () use ($url, $tags, $meta) {
57 4
            $shortUrl = new ShortUrl($url, $meta, $this->relationResolver);
58
            $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
59
60 4
            $this->verifyShortCodeUniqueness($meta, $shortUrl);
61 3
            $this->em->persist($shortUrl);
62 3
63 2
            return $shortUrl;
64 3
        });
65 3
    }
66 2
67 2
    private function findExistingShortUrlIfExists(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl
68
    {
69
        if (! $meta->findIfExists()) {
70 3
            return null;
71
        }
72
73 2
        /** @var ShortUrlRepositoryInterface $repo */
74
        $repo = $this->em->getRepository(ShortUrl::class);
75
        return $repo->findOneMatching($url, $tags, $meta);
76 12
    }
77
78 12
    private function verifyShortCodeUniqueness(ShortUrlMeta $meta, ShortUrl $shortUrlToBeCreated): void
79 4
    {
80
        $couldBeMadeUnique = $this->shortCodeHelper->ensureShortCodeUniqueness(
81
            $shortUrlToBeCreated,
82
            $meta->hasCustomSlug(),
83 9
        );
84 9
85
        if (! $couldBeMadeUnique) {
86
            $domain = $shortUrlToBeCreated->getDomain();
87 4
            $domainAuthority = $domain !== null ? $domain->getAuthority() : null;
88
89 4
            throw NonUniqueSlugException::fromSlug($shortUrlToBeCreated->getShortCode(), $domainAuthority);
90
        }
91 4
    }
92
}
93