Passed
Push — master ( 54de5e...97aeae )
by Peter
02:25
created

ApiAbstract::handleGetSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Framework\Http\Controllers\Admin;
6
7
use AbterPhp\Framework\Databases\Queries\FoundRows;
8
use AbterPhp\Framework\Domain\Entities\IStringerEntity;
9
use AbterPhp\Framework\Domain\Entities\IToJsoner;
0 ignored issues
show
Bug introduced by
The type AbterPhp\Framework\Domain\Entities\IToJsoner was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use AbterPhp\Framework\Http\Service\Execute\RepoServiceAbstract;
11
use Opulence\Http\Requests\UploadedFile;
12
use Opulence\Http\Responses\Response;
13
use Opulence\Http\Responses\ResponseHeaders;
14
use Opulence\Orm\OrmException;
15
use Opulence\Routing\Controller;
16
use Psr\Log\LoggerInterface;
17
18
abstract class ApiAbstract extends Controller
19
{
20
    const LOG_MSG_CREATE_FAILURE = 'Creating %1$s failed.';
21
    const LOG_MSG_UPDATE_FAILURE = 'Updating %1$s with id "%2$s" failed.';
22
    const LOG_MSG_DELETE_FAILURE = 'Deleting %1$s with id "%2$s" failed.';
23
    const LOG_MSG_GET_FAILURE    = 'Retrieving %1$s with id "%2$s" failed.';
24
    const LOG_MSG_LIST_FAILURE   = 'Retrieving %1$s failed.';
25
26
    const LOG_CONTEXT_EXCEPTION  = 'Exception';
27
    const LOG_PREVIOUS_EXCEPTION = 'Previous exception #%d';
28
29
    const ENTITY_SINGULAR = '';
30
    const ENTITY_PLURAL   = '';
31
32
    /** @var LoggerInterface */
33
    protected $logger;
34
35
    /** @var RepoService */
0 ignored issues
show
Bug introduced by
The type AbterPhp\Framework\Http\...llers\Admin\RepoService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
    protected $repoService;
37
38
    /** @var FoundRows */
39
    protected $foundRows;
40
41
    /**
42
     * ApiAbstract constructor.
43
     *
44
     * @param LoggerInterface     $logger
45
     * @param RepoServiceAbstract $repoService
46
     * @param FoundRows           $foundRows
47
     */
48
    public function __construct(LoggerInterface $logger, RepoServiceAbstract $repoService, FoundRows $foundRows)
49
    {
50
        $this->logger      = $logger;
51
        $this->repoService = $repoService;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repoService of type AbterPhp\Framework\Http\...ute\RepoServiceAbstract is incompatible with the declared type AbterPhp\Framework\Http\...llers\Admin\RepoService of property $repoService.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
52
        $this->foundRows   = $foundRows;
53
    }
54
55
    /**
56
     * @param string $entityId
57
     *
58
     * @return Response
59
     */
60
    public function get(string $entityId): Response
61
    {
62
        try {
63
            $entity = $this->repoService->retrieveEntity($entityId);
64
        } catch (\Exception $e) {
65
            $msg = sprintf(static::LOG_MSG_GET_FAILURE, static::ENTITY_SINGULAR);
66
67
            return $this->handleException($msg, $e);
68
        }
69
70
        return $this->handleGetSuccess($entity);
71
    }
72
73
    /**
74
     * @return Response
75
     */
76
    public function list(): Response
77
    {
78
        $query = $this->request->getQuery();
79
80
        $offset = (int)$query->get('offset', 0);
81
        $limit  = (int)$query->get('limit', 100);
82
83
        try {
84
            $entities = $this->repoService->retrieveList($offset, $limit, [], [], []);
85
        } catch (\Exception $e) {
86
            $msg = sprintf(static::LOG_MSG_LIST_FAILURE, static::ENTITY_PLURAL);
87
88
            return $this->handleException($msg, $e);
89
        }
90
91
        $maxCount = $this->foundRows->get();
92
93
        return $this->handleListSuccess($entities, $maxCount);
94
    }
95
96
    /**
97
     * @return Response
98
     */
99
    public function create(): Response
100
    {
101
        $data = $this->getCreateData();
102
103
        $errors = $this->repoService->validateForm($data);
104
105
        if (count($errors) > 0) {
106
            $msg = sprintf(static::LOG_MSG_CREATE_FAILURE, static::ENTITY_SINGULAR);
107
108
            return $this->handleErrors($msg, $errors);
109
        }
110
111
        try {
112
            $fileData = $this->getFileData($data);
113
            $entity   = $this->repoService->create($data, $fileData);
114
        } catch (\Exception $e) {
115
            $msg = sprintf(static::LOG_MSG_CREATE_FAILURE, static::ENTITY_SINGULAR);
116
117
            return $this->handleException($msg, $e);
118
        }
119
120
        return $this->handleCreateSuccess($entity);
121
    }
122
123
    /**
124
     * @param string $entityId
125
     *
126
     * @return Response
127
     */
128
    public function update(string $entityId): Response
129
    {
130
        $data = $this->getUpdateData();
131
132
        $errors = $this->repoService->validateForm($data);
133
134
        if (count($errors) > 0) {
135
            $msg = sprintf(static::LOG_MSG_UPDATE_FAILURE, static::ENTITY_SINGULAR, $entityId);
136
137
            return $this->handleErrors($msg, $errors);
138
        }
139
140
        try {
141
            $fileData = $this->getFileData($data);
142
            $entity   = $this->repoService->retrieveEntity($entityId);
143
            $this->repoService->update($entity, $data, $fileData);
144
        } catch (\Exception $e) {
145
            if ($this->isEntityNotFound($e)) {
146
                return $this->handleNotFound();
147
            }
148
149
            $msg = sprintf(static::LOG_MSG_UPDATE_FAILURE, static::ENTITY_SINGULAR, $entityId);
150
151
            return $this->handleException($msg, $e);
152
        }
153
154
        return $this->handleUpdateSuccess($entity);
155
    }
156
157
    /**
158
     * @param array $data
159
     *
160
     * @return UploadedFile[]
161
     */
162
    protected function getFileData(array $data): array
0 ignored issues
show
Unused Code introduced by
The parameter $data 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

162
    protected function getFileData(/** @scrutinizer ignore-unused */ array $data): array

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...
163
    {
164
        return [];
165
    }
166
167
    /**
168
     * @param string $entityId
169
     *
170
     * @return Response
171
     */
172
    public function delete(string $entityId): Response
173
    {
174
        try {
175
            $entity = $this->repoService->retrieveEntity($entityId);
176
            $this->repoService->delete($entity);
177
        } catch (\Exception $e) {
178
            if ($this->isEntityNotFound($e)) {
179
                return $this->handleNotFound();
180
            }
181
182
            $msg = sprintf(static::LOG_MSG_DELETE_FAILURE, static::ENTITY_SINGULAR, $entityId);
183
184
            return $this->handleException($msg, $e);
185
        }
186
187
        return $this->handleDeleteSuccess();
188
    }
189
190
    /**
191
     * @param \Exception $e
192
     *
193
     * @return bool
194
     */
195
    protected function isEntityNotFound(\Exception $e): bool
196
    {
197
        if (!($e instanceof OrmException)) {
198
            return false;
199
        }
200
201
        return $e->getMessage() === 'Failed to find entity';
202
    }
203
204
    /**
205
     * @return array
206
     */
207
    public function getCreateData(): array
208
    {
209
        return $this->getSharedData();
210
    }
211
212
    /**
213
     * @return array
214
     */
215
    public function getUpdateData(): array
216
    {
217
        return $this->getSharedData();
218
    }
219
220
    /**
221
     * @return array
222
     */
223
    public function getSharedData(): array
224
    {
225
        return $this->request->getJsonBody();
226
    }
227
228
    /**
229
     * @param string $msg
230
     * @param array  $errors
231
     *
232
     * @return Response
233
     */
234
    protected function handleErrors(string $msg, array $errors): Response
235
    {
236
        $this->logger->debug($msg);
237
238
        $response = new Response();
239
        $response->setStatusCode(ResponseHeaders::HTTP_BAD_REQUEST);
240
        $response->setContent(json_encode(['errors' => $errors]));
241
242
        return $response;
243
    }
244
245
    /**
246
     * @param string     $msg
247
     * @param \Exception $exception
248
     *
249
     * @return Response
250
     */
251
    protected function handleException(string $msg, \Exception $exception): Response
252
    {
253
        $this->logger->error($msg, $this->getExceptionContext($exception));
254
255
        $response = new Response();
256
        $response->setStatusCode(ResponseHeaders::HTTP_INTERNAL_SERVER_ERROR);
257
        $response->setContent(json_encode(['errors' => [$msg]]));
258
259
        return $response;
260
    }
261
262
    /**
263
     * @param \Exception $exception
264
     *
265
     * @return array
266
     */
267
    protected function getExceptionContext(\Exception $exception): array
268
    {
269
        $result = [static::LOG_CONTEXT_EXCEPTION => $exception->getMessage()];
270
271
        $i = 1;
272
        while ($exception = $exception->getPrevious()) {
273
            $result[sprintf(static::LOG_PREVIOUS_EXCEPTION, $i++)] = $exception->getMessage();
274
        }
275
276
        return $result;
277
    }
278
279
    /**
280
     * @param IStringerEntity $entity
281
     *
282
     * @return Response
283
     */
284
    protected function handleGetSuccess(IStringerEntity $entity): Response
285
    {
286
        $response = new Response();
287
        $response->setStatusCode(ResponseHeaders::HTTP_OK);
288
        $response->setContent($entity->toJSON());
289
290
        return $response;
291
    }
292
293
    /**
294
     * @param array $entities
295
     * @param int   $total
296
     *
297
     * @return Response
298
     */
299
    protected function handleListSuccess(array $entities, int $total): Response
300
    {
301
        $data = [];
302
        foreach ($entities as $entity) {
303
            $data[] = $entity->toJSON();
304
        }
305
        $content = sprintf('{"total":%d,"data":[%s]}', $total, implode(',', $data));
306
307
        $response = new Response();
308
        $response->setStatusCode(ResponseHeaders::HTTP_OK);
309
        $response->setContent($content);
310
311
        return $response;
312
    }
313
314
    /**
315
     * @param IStringerEntity $entity
316
     *
317
     * @return Response
318
     */
319
    protected function handleCreateSuccess(IStringerEntity $entity): Response
320
    {
321
        $response = new Response();
322
        $response->setStatusCode(ResponseHeaders::HTTP_CREATED);
323
        $response->setContent($entity->toJSON());
324
325
        return $response;
326
    }
327
328
    /**
329
     * @param IStringerEntity $entity
330
     *
331
     * @return Response
332
     */
333
    protected function handleUpdateSuccess(IStringerEntity $entity): Response
334
    {
335
        $response = new Response();
336
        $response->setStatusCode(ResponseHeaders::HTTP_OK);
337
        $response->setContent($entity->toJSON());
338
339
        return $response;
340
    }
341
342
    /**
343
     * @return Response
344
     */
345
    protected function handleDeleteSuccess(): Response
346
    {
347
        $response = new Response();
348
        $response->setStatusCode(ResponseHeaders::HTTP_NO_CONTENT);
349
350
        return $response;
351
    }
352
353
    /**
354
     * @return Response
355
     */
356
    protected function handleNotFound(): Response
357
    {
358
        $response = new Response();
359
        $response->setStatusCode(ResponseHeaders::HTTP_NOT_FOUND);
360
361
        return $response;
362
    }
363
}
364