Completed
Pull Request — 5.4 (#2626)
by
unknown
08:21 queued 01:22
created

SlugListener::getEntity()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 0
cp 0
rs 9.568
c 0
b 0
f 0
cc 4
nc 3
nop 2
crap 20
1
<?php
2
3
namespace Kunstmaan\NodeBundle\EventListener;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\NodeBundle\Controller\SlugActionInterface;
7
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
8
use Kunstmaan\NodeBundle\Entity\NodeVersion;
9
use Kunstmaan\NodeBundle\Event\Events;
10
use Kunstmaan\NodeBundle\Event\SlugSecurityEvent;
11
use Kunstmaan\NodeBundle\Repository\NodeVersionRepository;
12
use Kunstmaan\NodeBundle\Router\SlugRouter;
13
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
16
use Symfony\Component\HttpKernel\Event\ControllerEvent;
17
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
18
19
class SlugListener
20
{
21
    /**
22
     * @var EntityManager
23
     */
24
    protected $em;
25
26
    /**
27
     * @var ControllerResolverInterface
28
     */
29
    protected $resolver;
30
31
    /**
32
     * @var EventDispatcherInterface
33
     */
34
    protected $eventDispatcher;
35
36
    /**
37
     * SlugListener constructor.
38
     *
39
     * @param EntityManager               $em
40
     * @param ControllerResolverInterface $resolver
41
     * @param EventDispatcherInterface    $eventDispatcher
42
     */
43
    public function __construct(
44
        EntityManager $em,
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $em. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
45
        ControllerResolverInterface $resolver,
46
        EventDispatcherInterface $eventDispatcher
47
    ) {
48
        $this->em = $em;
49
        $this->resolver = $resolver;
50
        $this->eventDispatcher = $eventDispatcher;
51
    }
52
53
    /**
54
     * @param FilterControllerEvent|ControllerEvent $event
55
     *
56
     * @throws \Exception
57
     */
58
    public function onKernelController($event)
59
    {
60 View Code Duplication
        if (!$event instanceof FilterControllerEvent && !$event instanceof ControllerEvent) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
61
            throw new \InvalidArgumentException(\sprintf('Expected instance of type %s, %s given', \class_exists(ControllerEvent::class) ? ControllerEvent::class : FilterControllerEvent::class, \is_object($event) ? \get_class($event) : \gettype($event)));
62
        }
63
64
        $request = $event->getRequest();
65
66
        // Check if the event has a nodeTranslation, if not this method can be skipped
67
        if (!$request->attributes->has('_nodeTranslation')) {
68
            return;
69
        }
70
71
        $nodeTranslation = $request->attributes->get('_nodeTranslation');
72
        if (!($nodeTranslation instanceof NodeTranslation)) {
73
            throw new \Exception('Invalid _nodeTranslation value found in request attributes');
74
        }
75
76
        $entity = $this->getEntity($nodeTranslation, $request);
77
78
        // If the entity is an instance of the SlugActionInterface, change the controller
79
        if ($entity instanceof SlugActionInterface) {
80
            $request->attributes->set('_entity', $entity);
81
82
            // Do security check by firing an event that gets handled by the SlugSecurityListener
83
            $securityEvent = new SlugSecurityEvent();
84
            $securityEvent
85
                ->setNode($nodeTranslation->getNode())
86
                ->setEntity($entity)
87
                ->setRequest($request)
88
                ->setNodeTranslation($nodeTranslation);
89
90
            $this->eventDispatcher->dispatch(Events::SLUG_SECURITY, $securityEvent);
91
92
            // Set the right controller
93
            $request->attributes->set('_controller', $entity->getControllerAction());
94
            $event->setController($this->resolver->getController($request));
0 ignored issues
show
Security Bug introduced by
It seems like $this->resolver->getController($request) targeting Symfony\Component\HttpKe...erface::getController() can also be of type false; however, Symfony\Component\HttpKe...rEvent::setController() does only seem to accept callable, did you maybe forget to handle an error condition?
Loading history...
95
        }
96
    }
97
98
    /**
99
     * @param NodeTranslation $nodeTranslation
100
     * @param Request         $request
101
     *
102
     * @return null|object
103
     */
104
    private function getEntity(NodeTranslation $nodeTranslation, Request $request)
105
    {
106
        $versionId = $request->query->get('version');
107
108
        if ($request->attributes->get('_route') !== SlugRouter::$SLUG_PREVIEW || $versionId === null) {
109
            return $nodeTranslation->getRef($this->em);
110
        }
111
112
        /** @var NodeVersionRepository $nodeVersionRepository */
113
        $nodeVersionRepository = $this->em->getRepository(NodeVersion::class);
114
115
        $nodeVersion = $nodeVersionRepository->findOneBy([
116
            'nodeTranslation' => $nodeTranslation,
117
            'id' => $versionId,
118
        ]);
119
120
        if (!$nodeVersion instanceof NodeVersion) {
121
            return null;
122
        }
123
124
        return $nodeTranslation->getRefByNodeVersion($this->em, $nodeVersion);
125
    }
126
}
127