Completed
Push — master ( 7a82a2...eef030 )
by Pavel
02:12
created

FractalResponse::linkJsonApiResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php namespace Pz\Doctrine\Rest\Response;
2
3
use Pz\Doctrine\Rest\Contracts\JsonApiResource;
4
use League\Fractal\Manager;
5
use League\Fractal\Pagination\DoctrinePaginatorAdapter;
6
use League\Fractal\Resource\Collection;
7
use League\Fractal\Resource\Item;
8
use League\Fractal\Serializer\JsonApiSerializer;
9
use League\Fractal\TransformerAbstract;
10
11
use Pz\Doctrine\Rest\RestException;
12
use Pz\Doctrine\Rest\RestRequestAbstract;
13
use Pz\Doctrine\Rest\RestResponse;
14
use Pz\Doctrine\Rest\RestResponseFactory;
15
16
use Doctrine\ORM\QueryBuilder;
17
use Doctrine\ORM\Tools\Pagination\Paginator;
18
19
use Symfony\Component\HttpFoundation\Response;
20
21
class FractalResponse implements RestResponseFactory
22
{
23
    const JSON_API_CONTENT_TYPE = 'application/vnd.api+json';
24
25
    /**
26
     * @var TransformerAbstract
27
     */
28
    protected $transformer;
29
30
    /**
31
     * @var string|null
32
     */
33
    protected $baseUrl;
34
35
    /**
36
     * FractalResponse constructor.
37
     *
38
     * @param null $baseUrl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $baseUrl is correct as it would always require null to be passed?
Loading history...
39
     */
40 3
    public function __construct($baseUrl = null)
41
    {
42 3
        $this->baseUrl = $baseUrl;
43 3
    }
44
45
    /**
46
     * @param TransformerAbstract|null $transformer
47
     *
48
     * @return TransformerAbstract
49
     */
50 2
    public function transformer(TransformerAbstract $transformer = null)
51
    {
52 2
        if ($transformer !== null) {
53
            $this->transformer = $transformer;
54
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Pz\Doctrine\Rest\Response\FractalResponse which is incompatible with the documented return type League\Fractal\TransformerAbstract.
Loading history...
55
        }
56
57 2
        return $this->transformer;
58
    }
59
60
    /**
61
     * @param RestRequestAbstract   $request
62
     * @param QueryBuilder          $qb
63
     *
64
     * @return RestResponse
65
     */
66 2
    public function index(RestRequestAbstract $request, QueryBuilder $qb)
67
    {
68 2
        $headers = [];
69 2
        $paginator = new Paginator($qb, false);
70 2
        $resource = new Collection($paginator, $this->transformer(), $this->getIndexResourceKey($qb));
71 2
        $resource->setPaginator(new DoctrinePaginatorAdapter($paginator, $this->getPaginatorRouteGenerator($request)));
72
73 2
        if ($request->isAcceptJsonApi()) {
74 1
            $headers['Content-Type'] = static::JSON_API_CONTENT_TYPE;
75
        }
76
77 2
        return $this->response(
78 2
            $this->fractal($request)
79 2
                ->parseFieldsets($request->getFields())
80 2
                ->createData($resource)
81 2
                ->toArray(),
82 2
            RestResponse::HTTP_OK,
83 2
            $headers
84
        );
85
    }
86
87
    /**
88
     * @param RestRequestAbstract   $request
89
     * @param object                $entity
90
     *
91
     * @return RestResponse
92
     */
93 View Code Duplication
    public function show(RestRequestAbstract $request, $entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95
        $headers = [];
96
        $resourceKey = null;
97
98
        if ($entity instanceof JsonApiResource) {
99
            $resourceKey = $entity->getResourceKey();
100
101
            if ($request->isAcceptJsonApi()) {
102
                $headers['Location'] = $this->linkJsonApiResource($entity);
103
                $headers['Content-Type'] = static::JSON_API_CONTENT_TYPE;
104
            }
105
        }
106
107
        return $this->response(
108
            $this->fractal($request)
109
                ->parseFieldsets($request->getFields())
110
                ->createData(new Item($entity, $this->transformer(), $resourceKey ?? null))
111
                ->toArray(),
112
            RestResponse::HTTP_OK,
113
            $headers
114
        );
115
    }
116
117
    /**
118
     * @param RestRequestAbstract   $request
119
     * @param object                $entity
120
     *
121
     * @return RestResponse
122
     */
123 View Code Duplication
    public function create(RestRequestAbstract $request, $entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
    {
125
        $headers = [];
126
        $resourceKey = null;
127
128
        if ($entity instanceof JsonApiResource) {
129
            $resourceKey = $entity->getResourceKey();
130
131
            if ($request->isAcceptJsonApi()) {
132
                $headers['Location'] = $this->linkJsonApiResource($entity);
133
                $headers['Content-Type'] = static::JSON_API_CONTENT_TYPE;
134
            }
135
        }
136
137
        return $this->response(
138
            $this->fractal($request)
139
                ->parseFieldsets($request->getFields())
140
                ->createData(new Item($entity, $this->transformer(), $resourceKey))
141
                ->toArray(),
142
            Response::HTTP_CREATED,
143
            $headers
144
        );
145
    }
146
147
    /**
148
     * @param RestRequestAbstract $request
149
     * @param object              $entity
150
     *
151
     * @return RestResponse
152
     */
153 View Code Duplication
    public function update(RestRequestAbstract $request, $entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
154
    {
155
        $headers = [];
156
        $resourceKey = null;
157
158
        if ($entity instanceof JsonApiResource) {
159
            $resourceKey = $entity->getResourceKey();
160
161
            if ($request->isAcceptJsonApi()) {
162
                $headers['Location'] = $this->linkJsonApiResource($entity);
163
                $headers['Content-Type'] = static::JSON_API_CONTENT_TYPE;
164
            }
165
        }
166
167
        return $this->response(
168
            $this->fractal($request)
169
                ->parseFieldsets($request->getFields())
170
                ->createData(new Item($entity, $this->transformer(), $resourceKey))
171
                ->toArray(),
172
            Response::HTTP_OK,
173
            $headers
174
        );
175
    }
176
177
    /**
178
     * @param RestRequestAbstract $request
179
     * @param object              $entity
180
     *
181
     * @return RestResponse
182
     */
183
    public function delete(RestRequestAbstract $request, $entity)
184
    {
185
        return $this->response(null, RestResponse::HTTP_NO_CONTENT);
186
    }
187
188
    /**
189
     * @param RestRequestAbstract $request
190
     *
191
     * @return RestResponse
192
     */
193
    public function notFound(RestRequestAbstract $request)
194
    {
195
        return $this->response(null, RestResponse::HTTP_NOT_FOUND);
196
    }
197
198
    /**
199
     * @param \Error|\Exception|\Pz\Doctrine\Rest\RestException $exception
200
     *
201
     * @return RestResponse
202
     * @throws \Error|\Exception|RestException
203
     */
204
    public function exception($exception)
205
    {
206
        $message = $exception->getMessage();
207
208
        switch (true) {
209
            case ($exception instanceof RestException):
210
                $httpStatus = $exception->httpStatus();
211
                $errors = $exception->errors();
212
                break;
213
214
            default:
215
                throw $exception;
216
                // $httpStatus = RestResponse::HTTP_INTERNAL_SERVER_ERROR;
217
                // $errors = $exception->getTrace();
218
                break;
219
        }
220
221
        return $this->response(['message' => $message, 'errors' => $errors], $httpStatus);
222
    }
223
224
    /**
225
     * Return configured fractal by request format.
226
     *
227
     * @param RestRequestAbstract $request
228
     *
229
     * @return Manager
230
     */
231 2
    protected function fractal(RestRequestAbstract $request)
232
    {
233 2
        $fractal = new Manager();
234
235 2
        if ($request->isAcceptJsonApi()) {
236 1
            $fractal->setSerializer(new JsonApiSerializer($this->baseUrl));
237
        }
238
239 2
        if ($includes = $request->getInclude()) {
240
            $fractal->parseIncludes($includes);
241
        }
242
243 2
        if ($excludes = $request->getExclude()) {
244
            $fractal->parseExcludes($excludes);
245
        }
246
247 2
        return $fractal;
248
    }
249
250
    /**
251
     * @param mixed $data
252
     * @param int   $httStatus
253
     * @param array $headers
254
     *
255
     * @return RestResponse
256
     */
257 2
    protected function response($data = null, $httStatus = RestResponse::HTTP_OK, array $headers = [])
258
    {
259 2
        return new RestResponse($data, $httStatus, $headers);
260
    }
261
262
    /**
263
     * @param QueryBuilder $qb
264
     *
265
     * @return string
266
     */
267 2
    protected function getIndexResourceKey(QueryBuilder $qb)
268
    {
269 2
        $class = $qb->getRootEntities()[0];
270 2
        if (isset(class_implements($class)[JsonApiResource::class])) {
271
            return call_user_func($class . '::getResourceKey');
272
        }
273
274 2
        return $qb->getRootAliases()[0];
275
    }
276
277
    /**
278
     * @param JsonApiResource $resource
279
     *
280
     * @return string|null
281
     */
282
    protected function linkJsonApiResource(JsonApiResource $resource)
283
    {
284
        return sprintf('%s/%s/%s', $this->baseUrl, $resource->getResourceKey(), $resource->getId());
285
    }
286
287
    /**
288
     * @param RestRequestAbstract $request
289
     *
290
     * @return \Closure
291
     */
292
    protected function getPaginatorRouteGenerator(RestRequestAbstract $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

292
    protected function getPaginatorRouteGenerator(/** @scrutinizer ignore-unused */ RestRequestAbstract $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...
293
    {
294 2
        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

294
        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...
295 2
            return null;
296 2
        };
297
    }
298
}
299