Completed
Pull Request — master (#2831)
by
unknown
21:02 queued 05:51
created

RedirectSubscriber::onFlush()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
namespace Kunstmaan\RedirectBundle\EventSubscriber;
4
5
use Doctrine\Common\EventSubscriber;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\Event\OnFlushEventArgs;
8
use Doctrine\ORM\Events;
9
use Doctrine\ORM\UnitOfWork;
10
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
11
use Kunstmaan\RedirectBundle\Entity\AutoRedirectInterface;
12
use Kunstmaan\RedirectBundle\Entity\Redirect;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\RequestStack;
15
16
class RedirectSubscriber implements EventSubscriber
17
{
18
    /**
19
     * @var RequestStack
20
     */
21
    private $requestStack;
22
23
    /**
24
     * @var array<string,array<int,Redirect>
25
     */
26
    private $redirects = [];
27
28
    public function __construct(RequestStack $requestStack)
29
    {
30
        $this->requestStack = $requestStack;
31
    }
32
33
    public function getSubscribedEvents(): array
34
    {
35
        return [
36
            Events::onFlush,
37
        ];
38
    }
39
40
    public function onFlush(OnFlushEventArgs $onFlushEventArgs): void
41
    {
42
        $entityManager = $onFlushEventArgs->getEntityManager();
43
        $unitOfWork = $entityManager->getUnitOfWork();
44
45
        if (!$entityManager instanceof EntityManager) {
46
            return;
47
        }
48
49
        foreach ($unitOfWork->getScheduledEntityUpdates() as $entity) {
50
            if (!$entity instanceof NodeTranslation) {
51
                continue;
52
            }
53
54
            $this->createAutoRedirect(
55
                $entity,
56
                $entityManager,
57
                $unitOfWork
58
            );
59
        }
60
61
        $unitOfWork->computeChangeSets();
62
    }
63
64
    private function createAutoRedirect(
65
        NodeTranslation $nodeTranslation,
66
        EntityManager $entityManager,
67
        UnitOfWork $unitOfWork
68
    ): void {
69
        $changeSet = $unitOfWork->getEntityChangeSet($nodeTranslation);
70
71
        if (!isset($changeSet['slug'])) {
72
            return;
73
        }
74
75
        $page = $nodeTranslation->getRef($entityManager);
76
77
        if (!$page instanceof AutoRedirectInterface) {
78
            return;
79
        }
80
81
        $this->processRedirect(
82
            $entityManager,
83
            $nodeTranslation->getUrl(),
84
            $this->getNewUrl($nodeTranslation)
85
        );
86
    }
87
88
    private function getNewUrl(NodeTranslation $nodeTranslation): string
89
    {
90
        $newUrl = $nodeTranslation->getFullSlug();
91
92
        if (!is_string($newUrl)) {
93
            return '/';
94
        }
95
96
        return $this->startWithSlash($newUrl);
97
    }
98
99
    private function startWithSlash(string $url): string
100
    {
101
        $firstCharacter = $url[0] ?? '';
102
103
        if ($firstCharacter !== '/') {
104
            return '/' . $url;
105
        }
106
107
        return $url;
108
    }
109
110
    private function removeSlashAtStart(string $url): string
111
    {
112
        $firstCharacter = $url[0] ?? '';
113
114
        if ($firstCharacter !== '/') {
115
            return $url;
116
        }
117
118
        return substr($url, 1);
119
    }
120
121
    private function processRedirect(
122
        EntityManager $entityManager,
123
        string $oldUrl,
124
        string $newUrl
125
    ): void {
126
        $this->removeOriginRedirects(
127
            $newUrl,
128
            $entityManager
129
        );
130
131
        $this->updateTargetRedirects(
132
            $oldUrl,
133
            $newUrl,
134
            $entityManager
135
        );
136
137
        $this->createRedirect(
138
            $entityManager,
139
            $oldUrl,
140
            $newUrl
141
        );
142
    }
143
144
    private function removeOriginRedirects(
145
        string $newUrl,
146
        EntityManager $entityManager
147
    ): void {
148
        $origin = $this->removeSlashAtStart($newUrl);
149
150
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
151
            [
152
                'origin' => $origin,
153
            ]
154
        );
155
156
        /** @var Redirect $redirect */
157
        foreach ($redirects as $redirect) {
158
            $entityManager->remove($redirect);
159
        }
160
161
        if (isset($this->redirects[$origin])) {
162
            foreach ($this->redirects[$origin] as $redirect) {
163
                $entityManager->remove($redirect);
164
            }
165
        }
166
    }
167
168
    private function updateTargetRedirects(
169
        string $oldUrl,
170
        string $newUrl,
171
        EntityManager $entityManager
172
    ): void {
173
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
174
            [
175
                'target' => $this->startWithSlash($oldUrl),
176
            ]
177
        );
178
179
        /** @var Redirect $redirect */
180
        foreach ($redirects as $redirect) {
181
            $redirect->setTarget($newUrl);
182
            $redirect->setIsAutoRedirect(true);
183
        }
184
    }
185
186
    private function createRedirect(
187
        EntityManager $entityManager,
188
        string $oldUrl,
189
        string $newUrl
190
    ): void {
191
        $redirect = new Redirect();
192
        $redirect->setOrigin($oldUrl);
193
        $redirect->setTarget($newUrl);
194
        $redirect->setPermanent(true);
195
        $redirect->setDomain($this->getDomain());
196
        $redirect->setIsAutoRedirect(true);
197
198
        $entityManager->persist($redirect);
199
200
        $entityManager->getUnitOfWork()->computeChangeSet(
201
            $entityManager->getClassMetadata(Redirect::class),
202
            $redirect
203
        );
204
205
        if (!isset($this->redirects[$oldUrl])) {
206
            $this->redirects[$oldUrl] = [];
207
        }
208
209
        $this->redirects[$oldUrl][] = $redirect;
210
    }
211
212
    private function getDomain(): ?string
213
    {
214
        $request = $this->requestStack->getCurrentRequest();
215
216
        if (!$request instanceof Request) {
217
            return null;
218
        }
219
220
        return $request->getHost();
221
    }
222
}
223