Completed
Pull Request — master (#2831)
by
unknown
11:55
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
        if (!is_string($oldUrl)) {
132
            return;
133
        }
134
135
        $this->updateTargetRedirects(
136
            $oldUrl,
137
            $newUrl,
138
            $entityManager
139
        );
140
141
        $this->createRedirect(
142
            $entityManager,
143
            $oldUrl,
144
            $newUrl
145
        );
146
    }
147
148
    private function removeOriginRedirects(
149
        string $newUrl,
150
        EntityManager $entityManager
151
    ): void {
152
        $origin = $this->removeSlashAtStart($newUrl);
153
154
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
155
            [
156
                'origin' => $origin,
157
            ]
158
        );
159
160
        /** @var Redirect $redirect */
161
        foreach ($redirects as $redirect) {
162
            $entityManager->remove($redirect);
163
        }
164
165
        if (isset($this->redirects[$origin])) {
166
            foreach ($this->redirects[$origin] as $redirect) {
167
                $entityManager->remove($redirect);
168
            }
169
        }
170
    }
171
172
    private function updateTargetRedirects(
173
        string $oldUrl,
174
        string $newUrl,
175
        EntityManager $entityManager
176
    ): void {
177
        $redirects = $entityManager->getRepository(Redirect::class)->findBy(
178
            [
179
                'target' => $this->startWithSlash($oldUrl),
180
            ]
181
        );
182
183
        /** @var Redirect $redirect */
184
        foreach ($redirects as $redirect) {
185
            $redirect->setTarget($newUrl);
186
            $redirect->setIsAutoRedirect(true);
187
        }
188
    }
189
190
    private function createRedirect(
191
        EntityManager $entityManager,
192
        string $oldUrl,
193
        string $newUrl
194
    ): void {
195
        $redirect = new Redirect();
196
        $redirect->setOrigin($oldUrl);
197
        $redirect->setTarget($newUrl);
198
        $redirect->setPermanent(true);
199
        $redirect->setDomain($this->getDomain());
200
        $redirect->setIsAutoRedirect(true);
201
202
        $entityManager->persist($redirect);
203
204
        $entityManager->getUnitOfWork()->computeChangeSet(
205
            $entityManager->getClassMetadata(Redirect::class),
206
            $redirect
207
        );
208
209
        if (!isset($this->redirects[$oldUrl])) {
210
            $this->redirects[$oldUrl] = [];
211
        }
212
213
        $this->redirects[$oldUrl][] = $redirect;
214
    }
215
216
    private function getDomain(): ?string
217
    {
218
        $request = $this->requestStack->getCurrentRequest();
219
220
        if (!$request instanceof Request) {
221
            return null;
222
        }
223
224
        return $request->getHost();
225
    }
226
}
227