DeserializeListener   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 64
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 26
c 3
b 0
f 0
dl 0
loc 64
rs 10
wmc 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getSubscribedEvents() 0 4 1
A errorResponse() 0 5 1
B onKernelController() 0 33 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Damax\Common\Bridge\Symfony\Bundle\Listener;
6
7
use Damax\Common\Bridge\Symfony\Bundle\Annotation\Deserialize;
8
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9
use Symfony\Component\HttpFoundation\JsonResponse;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\HttpKernel\Event\ControllerEvent;
12
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
13
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
14
use Symfony\Component\HttpKernel\KernelEvents;
15
use Symfony\Component\Serializer\Exception\ExceptionInterface;
16
use Symfony\Component\Serializer\SerializerInterface;
17
use Symfony\Component\Validator\ConstraintViolationListInterface;
18
use Symfony\Component\Validator\Validator\ValidatorInterface;
19
20
class DeserializeListener implements EventSubscriberInterface
21
{
22
    private const CONTENT_TYPE = 'json';
23
24
    private $serializer;
25
    private $validator;
26
27
    public function __construct(SerializerInterface $serializer, ValidatorInterface $validator)
28
    {
29
        $this->serializer = $serializer;
30
        $this->validator = $validator;
31
    }
32
33
    public static function getSubscribedEvents(): array
34
    {
35
        return [
36
            KernelEvents::CONTROLLER => ['onKernelController', -16],
37
        ];
38
    }
39
40
    /**
41
     * @throws UnprocessableEntityHttpException
42
     * @throws BadRequestHttpException
43
     */
44
    public function onKernelController(ControllerEvent $event)
45
    {
46
        $request = $event->getRequest();
47
48
        if (!$request->attributes->get('_deserialize')) {
49
            return;
50
        }
51
52
        $contentType = $request->getRequestFormat(null) ?? $request->getContentType();
53
        if (self::CONTENT_TYPE !== $contentType) {
54
            return;
55
        }
56
57
        /** @var Deserialize $config */
58
        $config = $request->attributes->get('_deserialize');
59
60
        $context = $config->groups() ? ['groups' => $config->groups()] : [];
61
62
        // Deserialize body into object.
63
        try {
64
            $object = $this->serializer->deserialize($request->getContent(), $config->className(), self::CONTENT_TYPE, $context);
65
        } catch (ExceptionInterface $e) {
66
            throw new UnprocessableEntityHttpException('Invalid json.');
67
        }
68
69
        // Validate object and send problem response on failure.
70
        if ($config->validate() && count($violations = $this->validator->validate($object))) {
71
            $event->setController(function () use ($violations) {
72
                return $this->errorResponse($violations);
73
            });
74
        }
75
76
        $request->attributes->set($config->param(), $object);
77
    }
78
79
    private function errorResponse(ConstraintViolationListInterface $violations): Response
80
    {
81
        $data = $this->serializer->serialize($violations, self::CONTENT_TYPE);
82
83
        return JsonResponse::fromJsonString($data, Response::HTTP_BAD_REQUEST, ['Content-Type' => 'application/problem+json']);
84
    }
85
}
86