Completed
Push — develop ( 8d438a...1f78f5 )
by Alejandro
16s queued 12s
created

UrlShortener   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 81
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
eloc 39
c 0
b 0
f 0
dl 0
loc 81
rs 10
ccs 37
cts 37
cp 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
A findExistingShortUrlIfExists() 0 9 2
A verifyShortCodeUniqueness() 0 16 4
A urlToShortCode() 0 28 4
A __construct() 0 8 1
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\Domain\Resolver\DomainResolverInterface;
9
use Shlinkio\Shlink\Core\Entity\ShortUrl;
10
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
11
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
12
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
13
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
14
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
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 DomainResolverInterface $domainResolver;
26
27 12
    public function __construct(
28
        UrlValidatorInterface $urlValidator,
29
        EntityManagerInterface $em,
30
        DomainResolverInterface $domainResolver
31
    ) {
32 12
        $this->urlValidator = $urlValidator;
33 12
        $this->em = $em;
34 12
        $this->domainResolver = $domainResolver;
35
    }
36
37
    /**
38
     * @param string[] $tags
39
     * @throws NonUniqueSlugException
40
     * @throws InvalidUrlException
41
     * @throws Throwable
42
     */
43 12
    public function urlToShortCode(string $url, array $tags, ShortUrlMeta $meta): ShortUrl
44
    {
45
        // First, check if a short URL exists for all provided params
46 12
        $existingShortUrl = $this->findExistingShortUrlIfExists($url, $tags, $meta);
47 12
        if ($existingShortUrl !== null) {
48 8
            return $existingShortUrl;
49
        }
50
51 4
        $this->urlValidator->validateUrl($url);
52 4
        $this->em->beginTransaction();
53 4
        $shortUrl = new ShortUrl($url, $meta, $this->domainResolver);
54 4
        $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
55
56
        try {
57 4
            $this->verifyShortCodeUniqueness($meta, $shortUrl);
58 3
            $this->em->persist($shortUrl);
59 3
            $this->em->flush();
60 2
            $this->em->commit();
61 2
        } catch (Throwable $e) {
62 2
            if ($this->em->getConnection()->isTransactionActive()) {
63 1
                $this->em->rollback();
64 1
                $this->em->close();
65
            }
66
67 2
            throw $e;
68
        }
69
70 2
        return $shortUrl;
71
    }
72
73 12
    private function findExistingShortUrlIfExists(string $url, array $tags, ShortUrlMeta $meta): ?ShortUrl
74
    {
75 12
        if (! $meta->findIfExists()) {
76 4
            return null;
77
        }
78
79
        /** @var ShortUrlRepositoryInterface $repo */
80 8
        $repo = $this->em->getRepository(ShortUrl::class);
81 8
        return $repo->findOneMatching($url, $tags, $meta);
82
    }
83
84 4
    private function verifyShortCodeUniqueness(ShortUrlMeta $meta, ShortUrl $shortUrlToBeCreated): void
85
    {
86 4
        $shortCode = $shortUrlToBeCreated->getShortCode();
87 4
        $domain = $meta->getDomain();
88
89
        /** @var ShortUrlRepository $repo */
90 4
        $repo = $this->em->getRepository(ShortUrl::class);
91 4
        $otherShortUrlsExist = $repo->shortCodeIsInUse($shortCode, $domain);
92
93 4
        if ($otherShortUrlsExist && $meta->hasCustomSlug()) {
94 1
            throw NonUniqueSlugException::fromSlug($shortCode, $domain);
95
        }
96
97 3
        if ($otherShortUrlsExist) {
98 1
            $shortUrlToBeCreated->regenerateShortCode();
99 1
            $this->verifyShortCodeUniqueness($meta, $shortUrlToBeCreated);
100
        }
101
    }
102
}
103