Passed
Push — master ( d6fdae...fe741a )
by Michael
02:29
created

JsonApiViewListener::handleDocumentView()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
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\JsonApiDocumentView;
9
use Mikemirten\Bundle\JsonApiBundle\Response\JsonApiIteratorView;
10
use Mikemirten\Bundle\JsonApiBundle\Response\JsonApiObjectView;
11
use Mikemirten\Component\JsonApi\Document\AbstractDocument;
12
use Mikemirten\Component\JsonApi\Document\ErrorObject;
13
use Mikemirten\Component\JsonApi\Document\JsonApiObject;
14
use Mikemirten\Component\JsonApi\Document\NoDataDocument;
15
use Mikemirten\Component\JsonApi\Document\ResourceCollectionDocument;
16
use Mikemirten\Component\JsonApi\Document\ResourceObject;
17
use Mikemirten\Component\JsonApi\Document\SingleResourceDocument;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
20
21
/**
22
 * JsonApi view listener
23
 * Handles a JsonApi-document or its part responded by controller
24
 *
25
 * @package Mikemirten\Bundle\JsonApiBundle\EventListener
26
 */
27
class JsonApiViewListener
28
{
29
    /**
30
     * Object-handlers
31
     *
32
     * @var ObjectHandlerInterface[]
33
     */
34
    protected $objectHandlers = [];
35
36
    /**
37
     * Add object-handler
38
     *
39
     * @param ObjectHandlerInterface $handler
40
     */
41 2
    public function addObjectHandler(ObjectHandlerInterface $handler)
42
    {
43 2
        foreach ($handler->supports() as $class)
44
        {
45 2
            $this->objectHandlers[$class] = $handler;
46
        }
47 2
    }
48
49
    /**
50
     * On Kernel View event handler
51
     *
52
     * @param GetResponseForControllerResultEvent $event
53
     */
54 8
    public function onKernelView(GetResponseForControllerResultEvent $event)
55
    {
56 8
        $result = $event->getControllerResult();
57
58 8
        if ($result instanceof AbstractJsonApiView) {
59 5
            $event->setResponse($this->handleView($result));
60 3
            return;
61
        }
62
63 3
        if ($result instanceof AbstractDocument) {
64 1
            $event->setResponse($this->createResponse($result));
65 1
            return;
66
        }
67
68 2
        if ($result instanceof ResourceObject) {
69 1
            $event->setResponse($this->handleResource($result));
70 1
            return;
71
        }
72
73 1
        if ($result instanceof ErrorObject) {
74
            $event->setResponse($this->handleError($result));
75
        }
76 1
    }
77
78
    /**
79
     * Handle Json API view
80
     *
81
     * @param  AbstractJsonApiView $view
82
     * @return Response
83
     */
84 5
    protected function handleView(AbstractJsonApiView $view): Response
85
    {
86 5
        if ($view instanceof JsonApiDocumentView) {
87 1
            return $this->handleDocumentView($view);
88
        }
89
90 4
        if ($view instanceof JsonApiObjectView) {
91 2
            return $this->handleObjectView($view);
92
        }
93
94 2
        if ($view instanceof JsonApiIteratorView) {
95 1
            return $this->handleIteratorView($view);
96
        }
97
98 1
        throw new \LogicException(sprintf('Unsupported extension "%s" of JsonAPI View given.', get_class($view)));
99
    }
100
101
    /**
102
     * Handle document Json API view
103
     *
104
     * @param  JsonApiDocumentView $view
105
     * @return Response
106
     */
107 1
    protected function handleDocumentView(JsonApiDocumentView $view)
108
    {
109 1
        $document = $view->getDocument();
110 1
        $response = $this->createResponse($document);
111
112 1
        $this->handleResponseExtras($response, $view);
113 1
        return $response;
114
    }
115
116
    /**
117
     * Handle single object Json API view
118
     *
119
     * @param  JsonApiObjectView $view
120
     * @return Response
121
     */
122 2
    protected function handleObjectView(JsonApiObjectView $view): Response
123
    {
124 2
        $resource = $this->handleObject($view->getObject());
125 1
        $response = $this->handleResource($resource);
126
127 1
        $this->handleResponseExtras($response, $view);
128 1
        return $response;
129
    }
130
131
    /**
132
     * Handle object-iterator
133
     *
134
     * @param  JsonApiIteratorView $view
135
     * @return Response
136
     */
137 1
    protected function handleIteratorView(JsonApiIteratorView $view): Response
138
    {
139 1
        $document = new ResourceCollectionDocument();
140 1
        $document->setJsonApi(new JsonApiObject());
141
142 1
        foreach ($view as $object)
143
        {
144 1
            $resource = $this->handleObject($object);
145
146 1
            $document->addResource($resource);
147
        }
148
149 1
        $response = $this->createResponse($document);
150
151 1
        $this->handleResponseExtras($response, $view);
152 1
        return $response;
153
    }
154
155
    /**
156
     * Handle response data besides of the document itself
157
     * 
158
     * @param Response            $response
159
     * @param AbstractJsonApiView $view
160
     */
161 3
    protected function handleResponseExtras(Response $response, AbstractJsonApiView $view)
162
    {
163 3
        $response->setStatusCode($view->getStatus());
164 3
        $response->headers->add($view->getHeaders());
165 3
    }
166
167
    /**
168
     * Handle object
169
     *
170
     * @param  $object
171
     * @return ResourceObject
172
     */
173 3
    protected function handleObject($object): ResourceObject
174
    {
175 3
        $class = get_class($object);
176
        
177 3
        if (isset($this->objectHandlers[$class])) {
178 2
            return $this->objectHandlers[$class]->handle($object);
179
        }
180
181 1
        throw new \LogicException(sprintf('Unsupported instance of "%s" given.', $class));
182
    }
183
184
    /**
185
     * Handle single resource object
186
     *
187
     * @param  ResourceObject $resource
188
     * @return Response
189
     */
190 2
    protected function handleResource(ResourceObject $resource): Response
191
    {
192 2
        $document = new SingleResourceDocument($resource);
193 2
        $document->setJsonApi(new JsonApiObject());
194
195 2
        return $this->createResponse($document);
196
    }
197
198
    /**
199
     * Handle error
200
     *
201
     * @param  ErrorObject $error
202
     * @return Response
203
     */
204
    protected function handleError(ErrorObject $error): Response
205
    {
206
        $document = new NoDataDocument();
207
        $document->setJsonApi(new JsonApiObject());
208
        $document->addError($error);
209
210
        return $this->createResponse($document);
211
    }
212
213
    /**
214
     * Create response
215
     *
216
     * @param  AbstractDocument $document
217
     * @return Response
218
     */
219 5
    protected function createResponse(AbstractDocument $document): Response
220
    {
221 5
        $encoded  = $this->encode($document->toArray());
222 5
        $response = new Response($encoded);
223
224 5
        $response->headers->set('Content-Type', 'application/vnd.api+json');
225
226 5
        return $response;
227
    }
228
229
    /**
230
     * Encode object into a json-string
231
     *
232
     * @param  mixed $object
233
     * @return string
234
     * @throws \LogicException
235
     */
236 5
    protected function encode($object): string
237
    {
238 5
        $encoded = json_encode($object);
239
240 5
        if (json_last_error() === JSON_ERROR_NONE) {
241 5
            return $encoded;
242
        }
243
244
        throw new \LogicException('Encoding error: ' . json_last_error_msg());
245
    }
246
}