Passed
Push — master ( e26a9b...3863d1 )
by Michael
16:13
created

JsonApiViewListener::handlePostResourceCallback()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
crap 2
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 10
    public function onKernelView(GetResponseForControllerResultEvent $event)
62
    {
63 10
        $result = $event->getControllerResult();
64
65 10
        if (! is_object($result)) {
66 1
            return;
67
        }
68
69 9
        $response = $this->handleResult($result);
70
71 8
        if ($response !== null) {
72 8
            $event->setResponse($response);
73
        }
74 8
    }
75
76
    /**
77
     * Handle result
78
     *
79
     * @param  mixed $result
80
     * @return Response | null
81
     */
82 9
    protected function handleResult($result)
83
    {
84
        // Json API document
85 9
        if ($result instanceof AbstractDocument) {
86 1
            return $this->createResponse($result);
87
        }
88
89
        // Json API document wrapped into view
90 8
        if ($result instanceof JsonApiDocumentView) {
91 1
            return $this->handleDocumentView($result);
92
        }
93
94
        // An object for serialization wrapped into view
95 7
        if ($result instanceof JsonApiObjectView) {
96 3
            return $this->handleObjectView($result);
97
        }
98
99
        // An iterator of objects for serialization wrapped into view
100 4
        if ($result instanceof JsonApiIteratorView) {
101 2
            return $this->handleIteratorView($result);
102
        }
103
104
        // A resource-object of Json API document
105 2
        if ($result instanceof ResourceObject) {
106 1
            return $this->handleResource($result);
107
        }
108
109
        // An error-object of Json API document
110 1
        if ($result instanceof ErrorObject) {
111 1
            return $this->handleError($result);
112
        }
113
    }
114
115
    /**
116
     * Handle document Json API view
117
     *
118
     * @param  JsonApiDocumentView $view
119
     * @return Response
120
     */
121 1
    protected function handleDocumentView(JsonApiDocumentView $view)
122
    {
123 1
        $document = $view->getDocument();
124 1
        $response = $this->createResponse($document);
125
126 1
        $this->handleHttpAttributes($response, $view);
127 1
        return $response;
128
    }
129
130
    /**
131
     * Handle single object Json API view
132
     *
133
     * @param  JsonApiObjectView $view
134
     * @return Response
135
     */
136 3
    protected function handleObjectView(JsonApiObjectView $view): Response
137
    {
138 3
        $resource = $this->handleObject($view->getObject());
139 2
        $this->handlePostResourceCallback($view, $resource);
140
141 2
        $document = new SingleResourceDocument($resource);
142 2
        $document->setJsonApi(new JsonApiObject());
143
144 2
        $this->handleIncludedResources($document, $view);
145
146 2
        $response = $this->createResponse($document);
147
148 2
        $this->handleHttpAttributes($response, $view);
149 2
        return $response;
150
    }
151
152
    /**
153
     * Handle a callback after a resource-object has created.
154
     *
155
     * @param AbstractJsonApiView $view
156
     * @param ResourceObject      $resource
157
     */
158 4
    protected function handlePostResourceCallback(AbstractJsonApiView $view, ResourceObject $resource)
159
    {
160 4
        if ($view->hasPostResourceCallback()) {
161 2
            $callback = $view->getPostResourceCallback();
162 2
            $callback($resource);
163
        }
164 4
    }
165
166
    /**
167
     * Handle supposed to be included to document resources
168
     *
169
     * @param AbstractDocument              $document
170
     * @param IncludedObjectsAwareInterface $view
171
     */
172 4
    protected function handleIncludedResources(AbstractDocument $document, IncludedObjectsAwareInterface $view)
173
    {
174 4
        foreach ($view->getIncludedObjects() as $object)
175
        {
176 2
            $resource = $this->handleObject($object);
177 2
            $document->addIncludedResource($resource);
178
        }
179 4
    }
180
181
    /**
182
     * Handle object-iterator
183
     *
184
     * @param  JsonApiIteratorView $view
185
     * @return Response
186
     */
187 2
    protected function handleIteratorView(JsonApiIteratorView $view): Response
188
    {
189 2
        $document = new ResourceCollectionDocument();
190 2
        $document->setJsonApi(new JsonApiObject());
191
192 2
        foreach ($view as $object)
193
        {
194 2
            $resource = $this->handleObject($object);
195 2
            $this->handlePostResourceCallback($view, $resource);
196
197 2
            $document->addResource($resource);
198
        }
199
200 2
        $this->handleIncludedResources($document, $view);
201
202 2
        $response = $this->createResponse($document);
203
204 2
        $this->handleHttpAttributes($response, $view);
205 2
        return $response;
206
    }
207
208
    /**
209
     * Handle response data besides of the document itself
210
     * 
211
     * @param Response                     $response
212
     * @param HttpAttributesAwareInterface $view
213
     */
214 5
    protected function handleHttpAttributes(Response $response, HttpAttributesAwareInterface $view)
215
    {
216 5
        $response->setStatusCode($view->getStatusCode());
217 5
        $response->headers->add($view->getHeaders());
218 5
    }
219
220
    /**
221
     * Handle object
222
     *
223
     * @param  $object
224
     * @return ResourceObject
225
     */
226 5
    protected function handleObject($object): ResourceObject
227
    {
228 5
        if ($object instanceof ResourceObject) {
229 2
            return $object;
230
        }
231
232 3
        $class = get_class($object);
233
234 3
        return $this->getHandler($class)->handle($object);
235
    }
236
237
    /**
238
     * Get handler supports given class
239
     *
240
     * @param  string $class
241
     * @return ObjectHandlerInterface
242
     * @throws \LogicException
243
     */
244 3
    protected function getHandler(string $class): ObjectHandlerInterface
245
    {
246 3
        if (isset($this->resolvedObjectHandlers[$class])) {
247 2
            return $this->resolvedObjectHandlers[$class];
248
        }
249
250 3
        foreach ($this->objectHandlers as $handler) {
251 2
            if ($handler->supports($class)) {
252 2
                $this->resolvedObjectHandlers[$class] = $handler;
253 2
                return $handler;
254
            }
255
        }
256
257 1
        throw new \LogicException(sprintf('Class "%s" is not supported by known handles.', $class));
258
    }
259
260
    /**
261
     * Handle single resource object
262
     *
263
     * @param  ResourceObject $resource
264
     * @return Response
265
     */
266 1
    protected function handleResource(ResourceObject $resource): Response
267
    {
268 1
        $document = new SingleResourceDocument($resource);
269 1
        $document->setJsonApi(new JsonApiObject());
270
271 1
        return $this->createResponse($document);
272
    }
273
274
    /**
275
     * Handle error
276
     *
277
     * @param  ErrorObject $error
278
     * @return Response
279
     */
280 1
    protected function handleError(ErrorObject $error): Response
281
    {
282 1
        $document = new NoDataDocument();
283 1
        $document->setJsonApi(new JsonApiObject());
284 1
        $document->addError($error);
285
286 1
        return $this->createResponse($document);
287
    }
288
289
    /**
290
     * Create response
291
     *
292
     * @param  AbstractDocument $document
293
     * @return Response
294
     */
295 8
    protected function createResponse(AbstractDocument $document): Response
296
    {
297 8
        $encoded  = $this->encode($document->toArray());
298 8
        $response = new Response($encoded);
299
300 8
        $response->headers->set('Content-Type', 'application/vnd.api+json');
301
302 8
        return $response;
303
    }
304
305
    /**
306
     * Encode object into a json-string
307
     *
308
     * @param  mixed $object
309
     * @return string
310
     * @throws \LogicException
311
     */
312 8
    protected function encode($object): string
313
    {
314 8
        $encoded = json_encode($object);
315
316 8
        if (json_last_error() === JSON_ERROR_NONE) {
317 8
            return $encoded;
318
        }
319
320
        throw new \LogicException('Encoding error: ' . json_last_error_msg());
321
    }
322
}