Passed
Push — master ( 59e178...7dbd33 )
by Michael
02:26
created

JsonApiViewListener::handleHttpAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Bundle\JsonApiBundle\EventListener;
5
6
use Mikemirten\Bundle\JsonApiBundle\ObjectHandler\ObjectHandlerInterface;
7
use Mikemirten\Bundle\JsonApiBundle\Response\AbstractJsonApiView;
8
use Mikemirten\Bundle\JsonApiBundle\Response\Behaviour\HttpAttributesAwareInterface;
9
use Mikemirten\Bundle\JsonApiBundle\Response\Behaviour\IncludedObjectsAwareInterface;
10
use Mikemirten\Bundle\JsonApiBundle\Response\Behaviour\IncludedObjectsContainer;
11
use Mikemirten\Bundle\JsonApiBundle\Response\JsonApiDocumentView;
12
use Mikemirten\Bundle\JsonApiBundle\Response\JsonApiIteratorView;
13
use Mikemirten\Bundle\JsonApiBundle\Response\JsonApiObjectView;
14
use Mikemirten\Component\JsonApi\Document\AbstractDocument;
15
use Mikemirten\Component\JsonApi\Document\ErrorObject;
16
use Mikemirten\Component\JsonApi\Document\JsonApiObject;
17
use Mikemirten\Component\JsonApi\Document\NoDataDocument;
18
use Mikemirten\Component\JsonApi\Document\ResourceCollectionDocument;
19
use Mikemirten\Component\JsonApi\Document\ResourceObject;
20
use Mikemirten\Component\JsonApi\Document\SingleResourceDocument;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
23
24
/**
25
 * JsonApi view listener
26
 * Handles a JsonApi-document or its part responded by controller
27
 *
28
 * @package Mikemirten\Bundle\JsonApiBundle\EventListener
29
 */
30
class JsonApiViewListener
31
{
32
    /**
33
     * Object-handlers
34
     *
35
     * @var ObjectHandlerInterface[]
36
     */
37
    protected $objectHandlers = [];
38
39
    /**
40
     * Object-handlers resolved by supported class
41
     *
42
     * @var ObjectHandlerInterface[]
43
     */
44
    protected $resolvedObjectHandlers = [];
45
46
    /**
47
     * Add object-handler
48
     *
49
     * @param ObjectHandlerInterface $handler
50
     */
51 2
    public function addObjectHandler(ObjectHandlerInterface $handler)
52
    {
53 2
        $this->objectHandlers[] = $handler;
54 2
    }
55
56
    /**
57
     * On Kernel View event handler
58
     *
59
     * @param GetResponseForControllerResultEvent $event
60
     */
61 8
    public function onKernelView(GetResponseForControllerResultEvent $event)
62
    {
63 8
        $result = $event->getControllerResult();
64
65 8
        if (! is_object($result)) {
66 1
            return;
67
        }
68
69 7
        $response = $this->handleResult($result);
70
71 6
        if ($response !== null) {
72 6
            $event->setResponse($response);
73
        }
74 6
    }
75
76
    /**
77
     * Handle result
78
     *
79
     * @param  mixed $result
80
     * @return Response | null
81
     */
82 7
    public function handleResult($result)
83
    {
84
        switch (true)
85
        {
86
            // Json API document
87 7
            case ($result instanceof AbstractDocument):
88 1
                return $this->createResponse($result);
89
90
            // Json API document wrapped into view
91
            case ($result instanceof JsonApiDocumentView):
92 1
                return $this->handleDocumentView($result);
93
94
            // An object for serialization wrapped into view
95
            case ($result instanceof JsonApiObjectView):
96 2
                return $this->handleObjectView($result);
97
98
            // An iterator of objects for serialization wrapped into view
99
            case ($result instanceof JsonApiIteratorView):
100 1
                return $this->handleIteratorView($result);
101
102
            // A resource-object of Json API document
103
            case ($result instanceof ResourceObject):
104 1
                return $this->handleResource($result);
105
106
            // An error-object of Json API document
107
            case ($result instanceof ErrorObject):
108 1
                return $this->handleError($result);
109
        }
110
    }
111
112
    /**
113
     * Handle document Json API view
114
     *
115
     * @param  JsonApiDocumentView $view
116
     * @return Response
117
     */
118 1
    protected function handleDocumentView(JsonApiDocumentView $view)
119
    {
120 1
        $document = $view->getDocument();
121 1
        $response = $this->createResponse($document);
122
123 1
        $this->handleHttpAttributes($response, $view);
124 1
        return $response;
125
    }
126
127
    /**
128
     * Handle single object Json API view
129
     *
130
     * @param  JsonApiObjectView $view
131
     * @return Response
132
     */
133 2
    protected function handleObjectView(JsonApiObjectView $view): Response
134
    {
135 2
        $resource = $this->handleObject($view->getObject());
136
137 1
        $document = new SingleResourceDocument($resource);
138 1
        $document->setJsonApi(new JsonApiObject());
139
140 1
        $this->handleIncludedResources($document, $view);
141
142 1
        $response = $this->createResponse($document);
143
144 1
        $this->handleHttpAttributes($response, $view);
145 1
        return $response;
146
    }
147
148
    /**
149
     * Handle supposed to be included to document resources
150
     *
151
     * @param AbstractDocument              $document
152
     * @param IncludedObjectsAwareInterface $view
153
     */
154 2
    protected function handleIncludedResources(AbstractDocument $document, IncludedObjectsAwareInterface $view)
155
    {
156 2
        foreach ($view->getIncludedObjects() as $object)
157
        {
158 2
            $resource = $this->handleObject($object);
159 2
            $document->addIncludedResource($resource);
160
        }
161 2
    }
162
163
    /**
164
     * Handle object-iterator
165
     *
166
     * @param  JsonApiIteratorView $view
167
     * @return Response
168
     */
169 1
    protected function handleIteratorView(JsonApiIteratorView $view): Response
170
    {
171 1
        $document = new ResourceCollectionDocument();
172 1
        $document->setJsonApi(new JsonApiObject());
173
174 1
        foreach ($view as $object)
175
        {
176 1
            $resource = $this->handleObject($object);
177 1
            $document->addResource($resource);
178
        }
179
180 1
        $this->handleIncludedResources($document, $view);
181
182 1
        $response = $this->createResponse($document);
183
184 1
        $this->handleHttpAttributes($response, $view);
185 1
        return $response;
186
    }
187
188
    /**
189
     * Handle response data besides of the document itself
190
     * 
191
     * @param Response                     $response
192
     * @param HttpAttributesAwareInterface $view
193
     */
194 3
    protected function handleHttpAttributes(Response $response, HttpAttributesAwareInterface $view)
195
    {
196 3
        $response->setStatusCode($view->getStatusCode());
197 3
        $response->headers->add($view->getHeaders());
198 3
    }
199
200
    /**
201
     * Handle object
202
     *
203
     * @param  $object
204
     * @return ResourceObject
205
     */
206 3
    protected function handleObject($object): ResourceObject
207
    {
208 3
        if ($object instanceof ResourceObject) {
209
            return $object;
210
        }
211
212 3
        $class = get_class($object);
213
214 3
        return $this->getHandler($class)->handle($object);
215
    }
216
217
    /**
218
     * Get handler supports given class
219
     *
220
     * @param  string $class
221
     * @return ObjectHandlerInterface
222
     * @throws \LogicException
223
     */
224 3
    protected function getHandler(string $class): ObjectHandlerInterface
225
    {
226 3
        if (isset($this->resolvedObjectHandlers[$class])) {
227 2
            return $this->resolvedObjectHandlers[$class];
228
        }
229
230 3
        foreach ($this->objectHandlers as $handler) {
231 2
            if ($handler->supports($class)) {
232 2
                $this->resolvedObjectHandlers[$class] = $handler;
233 2
                return $handler;
234
            }
235
        }
236
237 1
        throw new \LogicException(sprintf('Class "%s" is not supported by known handles.', $class));
238
    }
239
240
    /**
241
     * Handle single resource object
242
     *
243
     * @param  ResourceObject $resource
244
     * @return Response
245
     */
246 1
    protected function handleResource(ResourceObject $resource): Response
247
    {
248 1
        $document = new SingleResourceDocument($resource);
249 1
        $document->setJsonApi(new JsonApiObject());
250
251 1
        return $this->createResponse($document);
252
    }
253
254
    /**
255
     * Handle error
256
     *
257
     * @param  ErrorObject $error
258
     * @return Response
259
     */
260 1
    protected function handleError(ErrorObject $error): Response
261
    {
262 1
        $document = new NoDataDocument();
263 1
        $document->setJsonApi(new JsonApiObject());
264 1
        $document->addError($error);
265
266 1
        return $this->createResponse($document);
267
    }
268
269
    /**
270
     * Create response
271
     *
272
     * @param  AbstractDocument $document
273
     * @return Response
274
     */
275 6
    protected function createResponse(AbstractDocument $document): Response
276
    {
277 6
        $encoded  = $this->encode($document->toArray());
278 6
        $response = new Response($encoded);
279
280 6
        $response->headers->set('Content-Type', 'application/vnd.api+json');
281
282 6
        return $response;
283
    }
284
285
    /**
286
     * Encode object into a json-string
287
     *
288
     * @param  mixed $object
289
     * @return string
290
     * @throws \LogicException
291
     */
292 6
    protected function encode($object): string
293
    {
294 6
        $encoded = json_encode($object);
295
296 6
        if (json_last_error() === JSON_ERROR_NONE) {
297 6
            return $encoded;
298
        }
299
300
        throw new \LogicException('Encoding error: ' . json_last_error_msg());
301
    }
302
}