Completed
Pull Request — master (#2831)
by
unknown
14:51
created

RedirectSubscriber::getSubscribedEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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
        $oldUrl = $nodeTranslation->getUrl();
82
83
        if (!is_string($oldUrl)) {
84
            return;
85
        }
86
87
        $this->processRedirect(
88
            $entityManager,
89
            $oldUrl,
90
            $this->getNewUrl($nodeTranslation)
91
        );
92
    }
93
94
    private function getNewUrl(NodeTranslation $nodeTranslation): string
95
    {
96
        $newUrl = $nodeTranslation->getFullSlug();
97
98
        if (!is_string($newUrl)) {
99
            return '/';
100
        }
101
102
        return $this->startWithSlash($newUrl);
103
    }
104
105
    private function startWithSlash(string $url): string
106
    {
107
        $firstCharacter = $url[0] ?? '';
108
109
        if ($firstCharacter !== '/') {
110
            return '/' . $url;
111
        }
112
113
        return $url;
114
    }
115
116
    private function removeSlashAtStart(string $url): string
117
    {
118
        $firstCharacter = $url[0] ?? '';
119
120
        if ($firstCharacter !== '/') {
121
            return $url;
122
        }
123
124
        return substr($url, 1);
125
    }
126
127
    private function processRedirect(
128
        EntityManager $entityManager,
129
        string $oldUrl,
130
        string $newUrl
131
    ): void {
132
        $this->removeOriginRedirects(
133
            $newUrl,
134
            $entityManager
135
        );
136
137
        $this->updateTargetRedirects(
138
            $oldUrl,
139
            $newUrl,
140
            $entityManager
141
        );
142
143
        $this->createRedirect(
144
            $entityManager,
145
            $oldUrl,
146
            $newUrl
147
        );
148
    }
149
150
    private function removeOriginRedirects(
151
        string $newUrl,
152
        EntityManager $entityManager
153
    ): void {
154
        $origin = $this->removeSlashAtStart($newUrl);
155
156
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
157
            [
158
                'origin' => $origin,
159
            ]
160
        );
161
162
        /** @var Redirect $redirect */
163
        foreach ($redirects as $redirect) {
164
            $entityManager->remove($redirect);
165
        }
166
167
        if (isset($this->redirects[$origin])) {
168
            foreach ($this->redirects[$origin] as $redirect) {
169
                $entityManager->remove($redirect);
170
            }
171
        }
172
    }
173
174
    private function updateTargetRedirects(
175
        string $oldUrl,
176
        string $newUrl,
177
        EntityManager $entityManager
178
    ): void {
179
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
180
            [
181
                'target' => $this->startWithSlash($oldUrl),
182
            ]
183
        );
184
185
        /** @var Redirect $redirect */
186
        foreach ($redirects as $redirect) {
187
            $redirect->setTarget($newUrl);
188
            $redirect->setIsAutoRedirect(true);
189
        }
190
    }
191
192
    private function createRedirect(
193
        EntityManager $entityManager,
194
        string $oldUrl,
195
        string $newUrl
196
    ): void {
197
        $redirect = new Redirect();
198
        $redirect->setOrigin($oldUrl);
199
        $redirect->setTarget($newUrl);
200
        $redirect->setPermanent(true);
201
        $redirect->setDomain($this->getDomain());
202
        $redirect->setIsAutoRedirect(true);
203
204
        $entityManager->persist($redirect);
205
206
        $entityManager->getUnitOfWork()->computeChangeSet(
207
            $entityManager->getClassMetadata(Redirect::class),
208
            $redirect
209
        );
210
211
        if (!isset($this->redirects[$oldUrl])) {
212
            $this->redirects[$oldUrl] = [];
213
        }
214
215
        $this->redirects[$oldUrl][] = $redirect;
216
    }
217
218
    private function getDomain(): ?string
219
    {
220
        $request = $this->requestStack->getCurrentRequest();
221
222
        if (!$request instanceof Request) {
223
            return null;
224
        }
225
226
        return $request->getHost();
227
    }
228
}
229