Completed
Push — master ( 78334d...666726 )
by Pavel
01:41
created

FractalResponseFactory   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 251
Duplicated Lines 0 %

Test Coverage

Coverage 60.56%

Importance

Changes 0
Metric Value
dl 0
loc 251
ccs 43
cts 71
cp 0.6056
rs 10
c 0
b 0
f 0
wmc 25

14 Methods

Rating   Name   Duplication   Size   Complexity  
A deleted() 0 3 1
A collection() 0 20 2
A item() 0 5 2
A resourceResponse() 0 13 2
B fractal() 0 21 5
A getIndexResourceKey() 0 8 2
A created() 0 15 2
A linkJsonApiResource() 0 3 1
A exception() 0 12 2
A __construct() 0 4 1
A notFound() 0 3 1
A getPaginatorRouteGenerator() 0 4 1
A updated() 0 3 1
A transformer() 0 8 2
1
<?php namespace Pz\Doctrine\Rest\Response;
2
3
use League\Fractal\Manager;
4
use League\Fractal\Pagination\DoctrinePaginatorAdapter;
5
use League\Fractal\Resource\Collection;
6
use League\Fractal\Resource\Item;
7
use League\Fractal\Resource\ResourceInterface;
8
use League\Fractal\Serializer\JsonApiSerializer;
9
use League\Fractal\TransformerAbstract;
10
11
use Pz\Doctrine\Rest\RestException;
12
use Pz\Doctrine\Rest\RestRequest;
13
use Pz\Doctrine\Rest\RestResponse;
14
use Pz\Doctrine\Rest\RestResponseFactory;
15
use Pz\Doctrine\Rest\Contracts\JsonApiResource;
16
17
use Doctrine\ORM\QueryBuilder;
18
use Doctrine\ORM\Tools\Pagination\Paginator;
19
20
use Symfony\Component\HttpFoundation\Response;
21
22
class FractalResponseFactory implements RestResponseFactory
23
{
24
    const JSON_API_CONTENT_TYPE = 'application/vnd.api+json';
25
26
    /**
27
     * @var TransformerAbstract
28
     */
29
    protected $transformer;
30
31
    /**
32
     * @var string|null
33
     */
34
    protected $baseUrl;
35
36
    /**
37
     * FractalResponse constructor.
38
     *
39
     * @param string|null              $baseUrl
40
     * @param TransformerAbstract|null $transformer
41
     */
42 8
    public function __construct($baseUrl = null, TransformerAbstract $transformer = null)
43
    {
44 8
        $this->baseUrl = $baseUrl;
45 8
        $this->transformer($transformer);
46 8
    }
47
48
    /**
49
     * @param TransformerAbstract|null $transformer
50
     *
51
     * @return TransformerAbstract|$this
52
     */
53 8
    public function transformer(TransformerAbstract $transformer = null)
54
    {
55 8
        if ($transformer !== null) {
56 8
            $this->transformer = $transformer;
57 8
            return $this;
58
        }
59
60 8
        return $this->transformer;
61
    }
62
63
    /**
64
     * @param RestRequest  $request
65
     * @param QueryBuilder $qb
66
     *
67
     * @return RestResponse
68
     */
69 6
    public function collection(RestRequest $request, QueryBuilder $qb)
70
    {
71 6
        $resourceKey = $this->getIndexResourceKey($qb);
72 6
        $paginator = new Paginator($qb, false);
73 6
        $resource = new Collection($paginator, $this->transformer(), $resourceKey);
74
75 6
        if ($request->getLimit() !== null) {
76 2
            $resource->setPaginator(new DoctrinePaginatorAdapter($paginator,
77 2
                function(int $page) use ($resourceKey, $request) {
78 1
                    return "{$this->baseUrl}/$resourceKey?".http_build_query([
79
                        'page' => [
80 1
                            'number'    => $page,
81 1
                            'size'      => $request->getLimit()
82
                        ]
83
                    ]);
84 2
                }
85
            ));
86
        }
87
88 6
        return $this->resourceResponse($request, $resource);
89
    }
90
91
    /**
92
     * @param RestRequest $request
93
     * @param object      $entity
94
     *
95
     * @return RestResponse
96
     */
97 2
    public function item(RestRequest $request, $entity)
98
    {
99 2
        $resourceKey = ($entity instanceof JsonApiResource) ? $entity->getResourceKey() : null;
100
101 2
        return $this->resourceResponse($request, new Item($entity, $this->transformer(), $resourceKey));
102
    }
103
104
    /**
105
     * @param RestRequest $request
106
     * @param object      $entity
107
     *
108
     * @return RestResponse
109
     */
110
    public function created(RestRequest $request, $entity)
111
    {
112
        $headers = [];
113
        $resourceKey = null;
114
115
        if ($entity instanceof JsonApiResource) {
116
            $resourceKey = $entity->getResourceKey();
117
            $headers['Location'] = $this->linkJsonApiResource($entity);
118
        }
119
120
        return $this->resourceResponse(
121
            $request,
122
            new Item($entity, $this->transformer(), $resourceKey),
123
            Response::HTTP_CREATED,
124
            $headers
125
        );
126
    }
127
128
    /**
129
     * @param RestRequest $request
130
     * @param object      $entity
131
     *
132
     * @return RestResponse
133
     */
134
    public function updated(RestRequest $request, $entity)
135
    {
136
        return $this->item($request, $entity);
137
    }
138
139
    /**
140
     * @param RestRequest $request
141
     * @param object      $entity
142
     *
143
     * @return RestResponse
144
     */
145
    public function deleted(RestRequest $request, $entity)
146
    {
147
        return $this->resourceResponse(null, RestResponse::HTTP_NO_CONTENT);
0 ignored issues
show
Bug introduced by
null of type null is incompatible with the type Pz\Doctrine\Rest\RestRequest expected by parameter $request of Pz\Doctrine\Rest\Respons...ory::resourceResponse(). ( Ignorable by Annotation )

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

147
        return $this->resourceResponse(/** @scrutinizer ignore-type */ null, RestResponse::HTTP_NO_CONTENT);
Loading history...
Bug introduced by
Pz\Doctrine\Rest\RestResponse::HTTP_NO_CONTENT of type integer is incompatible with the type League\Fractal\Resource\ResourceInterface|null expected by parameter $resource of Pz\Doctrine\Rest\Respons...ory::resourceResponse(). ( Ignorable by Annotation )

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

147
        return $this->resourceResponse(null, /** @scrutinizer ignore-type */ RestResponse::HTTP_NO_CONTENT);
Loading history...
148
    }
149
150
    /**
151
     * @param RestRequest $request
152
     *
153
     * @return RestResponse
154
     */
155
    public function notFound(RestRequest $request)
156
    {
157
        return $this->resourceResponse(null, RestResponse::HTTP_NOT_FOUND);
0 ignored issues
show
Bug introduced by
Pz\Doctrine\Rest\RestResponse::HTTP_NOT_FOUND of type integer is incompatible with the type League\Fractal\Resource\ResourceInterface|null expected by parameter $resource of Pz\Doctrine\Rest\Respons...ory::resourceResponse(). ( Ignorable by Annotation )

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

157
        return $this->resourceResponse(null, /** @scrutinizer ignore-type */ RestResponse::HTTP_NOT_FOUND);
Loading history...
Bug introduced by
null of type null is incompatible with the type Pz\Doctrine\Rest\RestRequest expected by parameter $request of Pz\Doctrine\Rest\Respons...ory::resourceResponse(). ( Ignorable by Annotation )

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

157
        return $this->resourceResponse(/** @scrutinizer ignore-type */ null, RestResponse::HTTP_NOT_FOUND);
Loading history...
158
    }
159
160
    /**
161
     * @param \Error|\Exception|\Pz\Doctrine\Rest\RestException $exception
162
     *
163
     * @return RestResponse
164
     * @throws \Error|\Exception|RestException
165
     */
166
    /**
167
     * @param \Error|\Exception|RestException $exception
168
     *
169
     * @return static
170
     * @throws \Error|\Exception|RestException
171
     */
172
    public function exception($exception)
173
    {
174
        switch (true) {
175
            case ($exception instanceof RestException):
176
                return RestResponse::create(['errors' => $exception->errors()], $exception->httpStatus());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Pz\Doctrine\Rest\...xception->httpStatus()) returns the type Pz\Doctrine\Rest\RestResponse which is incompatible with the documented return type Pz\Doctrine\Rest\Response\FractalResponseFactory.
Loading history...
177
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
178
179
            default:
180
                break;
181
        }
182
183
        throw $exception;
184
    }
185
186
    /**
187
     * Return configured fractal by request format.
188
     *
189
     * @param RestRequest $request
190
     *
191
     * @return Manager
192
     */
193 8
    protected function fractal(RestRequest $request)
194
    {
195 8
        $fractal = new Manager();
196
197 8
        if ($request->isAcceptJsonApi()) {
198 8
            $fractal->setSerializer(new JsonApiSerializer($this->baseUrl));
199
        }
200
201 8
        if ($includes = $request->getInclude()) {
202 3
            $fractal->parseIncludes($includes);
203
        }
204
205 8
        if ($excludes = $request->getExclude()) {
206
            $fractal->parseExcludes($excludes);
207
        }
208
209 8
        if ($fields = $request->getFields()) {
210 3
            $fractal->parseFieldsets($fields);
211
        }
212
213 8
        return $fractal;
214
    }
215
216
    /**
217
     * @param RestRequest            $request
218
     * @param ResourceInterface|null $resource
219
     * @param int                    $httStatus
220
     * @param array                  $headers
221
     *
222
     * @return RestResponse
223
     */
224 8
    protected function resourceResponse(
225
        RestRequest $request,
226
        ResourceInterface $resource = null,
227
        $httStatus = RestResponse::HTTP_OK,
228
        array $headers = []
229
    ) {
230 8
        if ($request->isAcceptJsonApi()) {
231 8
            $headers['Content-Type'] = static::JSON_API_CONTENT_TYPE;
232
        }
233
234 8
        $data = $this->fractal($request)->createData($resource)->toArray();
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type null; however, parameter $resource of League\Fractal\Manager::createData() does only seem to accept League\Fractal\Resource\ResourceInterface, 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

234
        $data = $this->fractal($request)->createData(/** @scrutinizer ignore-type */ $resource)->toArray();
Loading history...
235
236 8
        return RestResponse::create($data, $httStatus, $headers);
237
    }
238
239
    /**
240
     * @param QueryBuilder $qb
241
     *
242
     * @return string
243
     */
244 6
    protected function getIndexResourceKey(QueryBuilder $qb)
245
    {
246 6
        $class = $qb->getRootEntities()[0];
247 6
        if (isset(class_implements($class)[JsonApiResource::class])) {
248 6
            return call_user_func("$class::getResourceKey");
249
        }
250
251
        return $qb->getRootAliases()[0];
252
    }
253
254
    /**
255
     * @param JsonApiResource $resource
256
     *
257
     * @return string|null
258
     */
259
    protected function linkJsonApiResource(JsonApiResource $resource)
260
    {
261
        return sprintf('%s/%s/%s', $this->baseUrl, $resource->getResourceKey(), $resource->getId());
262
    }
263
264
    /**
265
     * @param RestRequest $request
266
     *
267
     * @return \Closure
268
     */
269
    protected function getPaginatorRouteGenerator(RestRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

269
    protected function getPaginatorRouteGenerator(/** @scrutinizer ignore-unused */ RestRequest $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
270
    {
271
        return function(int $page) {
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed. ( Ignorable by Annotation )

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

271
        return function(/** @scrutinizer ignore-unused */ int $page) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
272
            return null;
273
        };
274
    }
275
}
276