SymfonySerializerAdapter   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 50
c 1
b 0
f 0
dl 0
loc 142
rs 10
wmc 15

10 Methods

Rating   Name   Duplication   Size   Complexity  
A hydrateWithReflection() 0 7 1
A getSerializer() 0 3 1
A normalize() 0 4 1
A putData() 0 17 2
A toFormat() 0 5 2
A toResponse() 0 7 2
A postData() 0 15 2
A runLifeCycleEvent() 0 4 2
A __construct() 0 8 1
A decodeRequestBody() 0 4 1
1
<?php
2
3
namespace Apie\CorePlugin\ResourceSerializers;
4
5
use Apie\Core\Events\DecodeEvent;
6
use Apie\Core\Interfaces\FormatRetrieverInterface;
7
use Apie\Core\Interfaces\ResourceSerializerInterface;
8
use Apie\Core\PluginInterfaces\ResourceLifeCycleInterface;
9
use Apie\ObjectAccessNormalizer\ObjectAccess\ObjectAccess;
10
use Laminas\Diactoros\Response\TextResponse;
11
use Psr\Http\Message\ResponseInterface;
12
use Symfony\Component\Serializer\Serializer;
13
14
/**
15
 * Wrapper around Symfony Serializer.
16
 */
17
class SymfonySerializerAdapter implements ResourceSerializerInterface
18
{
19
    const INTERNAL_FOR_DATALAYER = 'datalayer';
20
21
    private $serializer;
22
23
    private $formatRetriever;
24
25
    private $resourceLifeCycles;
26
27
    /**
28
     * @param Serializer $serializer
29
     * @param FormatRetrieverInterface $formatRetriever
30
     * @param iterable<ResourceLifeCycleInterface> $resourceLifeCycles
31
     */
32
    public function __construct(
33
        Serializer $serializer,
34
        FormatRetrieverInterface $formatRetriever,
35
        iterable $resourceLifeCycles
36
    ) {
37
        $this->serializer = $serializer;
38
        $this->formatRetriever = $formatRetriever;
39
        $this->resourceLifeCycles = $resourceLifeCycles;
40
    }
41
42
    private function toFormat(?string $acceptHeader): string
43
    {
44
        return $acceptHeader === self::INTERNAL_FOR_DATALAYER
45
            ? self::INTERNAL_FOR_DATALAYER
46
            : ($this->formatRetriever->getFormat($acceptHeader) ?? 'json');
0 ignored issues
show
Bug introduced by
It seems like $acceptHeader can also be of type null; however, parameter $contentType of Apie\Core\Interfaces\For...rInterface::getFormat() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

46
            : ($this->formatRetriever->getFormat(/** @scrutinizer ignore-type */ $acceptHeader) ?? 'json');
Loading history...
47
    }
48
49
    /**
50
     * Helper method to call the method on all all lifecycle instances.
51
     *
52
     * @param string $event
53
     * @param mixed[] $args
54
     */
55
    private function runLifeCycleEvent(string $event, ...$args)
56
    {
57
        foreach ($this->resourceLifeCycles as $resourceLifeCycle) {
58
            $resourceLifeCycle->$event(...$args);
59
        }
60
    }
61
62
    /**
63
     * In case the symfony serializer is needed outside the adapter. It's highly discouraged to use
64
     * directly.
65
     *
66
     * @return Serializer
67
     */
68
    public function getSerializer(): Serializer
69
    {
70
        return $this->serializer;
71
    }
72
73
    /**
74
     * {@inheritDoc}
75
     */
76
    public function putData(object $resource, string $requestBody, string $contentType): object
77
    {
78
        $contentFormat = $this->toFormat($contentType);
79
        $event = new DecodeEvent($requestBody, $contentType, $resource, get_class($resource));
80
        $this->runLifeCycleEvent('onPreDecodeRequestBody', $event);
81
        if (!$event->hasDecodedData()) {
82
            $event->setDecodedData($this->decodeRequestBody($requestBody, $contentType));
83
        }
84
        $this->runLifeCycleEvent('onPostDecodeRequestBody', $event);
85
86
        return $this->serializer->denormalize(
87
            $event->getDecodedData(),
88
            get_class($resource),
89
            $contentFormat,
90
            [
91
                'groups' => ['base', 'write', 'put'],
92
                'object_to_populate' => $resource
93
            ]
94
        );
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100
    public function decodeRequestBody(string $requestBody, string $contentType)
101
    {
102
        $contentFormat = $this->toFormat($contentType);
103
        return $this->serializer->decode($requestBody, $contentFormat, []);
104
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109
    public function postData(string $resourceClass, string $requestBody, string $contentType): object
110
    {
111
        $contentFormat = $this->toFormat($contentType);
112
        $event = new DecodeEvent($requestBody, $contentType, null, $resourceClass);
113
        $this->runLifeCycleEvent('onPreDecodeRequestBody', $event);
114
        if (!$event->hasDecodedData()) {
115
            $event->setDecodedData($this->decodeRequestBody($requestBody, $contentType));
116
        }
117
        $this->runLifeCycleEvent('onPostDecodeRequestBody', $event);
118
        return $this->serializer->denormalize(
119
            $event->getDecodedData(),
120
            $resourceClass,
121
            $contentFormat,
122
            [
123
                'groups' => ['base', 'write', 'post'],
124
            ]
125
        );
126
    }
127
128
    /**
129
     * {@inheritDoc}
130
     */
131
    public function toResponse($resource, string $acceptHeader): ResponseInterface
132
    {
133
        $format = $this->toFormat($acceptHeader);
134
        $contentType = $this->formatRetriever->getContentType($format);
135
        $response = $this->serializer->serialize($resource, $format, ['groups' => ['base', 'read', 'get']]);
136
137
        return new TextResponse($response, is_null($resource) ? 204 : 200, ['content-type' => $contentType]);
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143
    public function normalize($resource, string $acceptHeader)
144
    {
145
        $format = $this->toFormat($acceptHeader);
146
        return $this->serializer->normalize($resource, $format, ['groups' => ['base', 'read', 'get']]);
147
    }
148
149
    /**
150
     * {@inheritDoc}
151
     */
152
    public function hydrateWithReflection(array $data, string $resourceClass)
153
    {
154
        return $this->serializer->denormalize(
155
            $data,
156
            $resourceClass,
157
            self::INTERNAL_FOR_DATALAYER,
158
            ['object_access' => new ObjectAccess(false, true), 'keep_setter_calls' => true]
159
        );
160
    }
161
}
162