Issues (150)

src/Extension/ApiController.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace SoliDry\Extension;
4
5
use Illuminate\Routing\Controller;
6
use Illuminate\Http\Request;
7
use Illuminate\Routing\Route;
8
use League\Fractal\Resource\Collection;
9
use SoliDry\Helpers\ConfigOptions;
10
use SoliDry\Blocks\EntitiesTrait;
11
use SoliDry\Containers\Response;
12
use Illuminate\Http\Response as IlluminateResponse;
13
use SoliDry\Types\HTTPMethodsInterface;
14
use SoliDry\Types\JwtInterface;
15
use SoliDry\Helpers\Json;
16
use SoliDry\Types\PhpInterface;
17
18
/**
19
 * Class ApiController
20
 *
21
 * @package SoliDry\Extension
22
 *
23
 * @property Response response
24
 */
25
class ApiController extends Controller implements JSONApiInterface
26
{
27
    use BaseRelationsTrait,
0 ignored issues
show
The trait SoliDry\Extension\JWTTrait requires the property $id which is not provided by SoliDry\Extension\ApiController.
Loading history...
28
        OptionsTrait,
29
        EntitiesTrait,
30
        JWTTrait,
31
        FsmTrait,
32
        SpellCheckTrait,
33
        BitMaskTrait,
34
        CacheTrait;
35
36
    // JSON API support enabled by default
37
    /**
38
     * @var bool
39
     */
40
    protected bool $jsonApi = true;
41
42
    /**
43
     * @var array
44
     */
45
    protected array $props = [];
46
47
    protected $entity;
48
49
    /**
50
     * @var BaseModel
51
     */
52
    protected BaseModel $model;
53
54
    /**
55
     * @var EntitiesTrait
56
     */
57
    private EntitiesTrait $modelEntity;
58
59
    protected $formRequest;
60
61
    /**
62
     * @var bool
63
     */
64
    private bool $relsRemoved    = false;
65
66
    /**
67
     * @var array
68
     */
69
    private array $defaultOrderBy = [];
70
71
    /**
72
     * @var ConfigOptions
73
     */
74
    protected ConfigOptions $configOptions;
75
76
    /**
77
     * @var CustomSql
78
     */
79
    protected CustomSql $customSql;
80
81
    /**
82
     * @var BitMask
83
     */
84
    private BitMask $bitMask;
85
86
    private $response;
87
88
    /**
89
     * @var array
90
     */
91
    private array $jsonApiMethods = [
92
        JSONApiInterface::URI_METHOD_INDEX,
93
        JSONApiInterface::URI_METHOD_VIEW,
94
        JSONApiInterface::URI_METHOD_CREATE,
95
        JSONApiInterface::URI_METHOD_UPDATE,
96
        JSONApiInterface::URI_METHOD_DELETE,
97
        JSONApiInterface::URI_METHOD_RELATIONS,
98
    ];
99
100
    /**
101
     * BaseControllerTrait constructor.
102
     *
103
     * @param Route $route
104
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
105
     * @throws \ReflectionException
106
     */
107
    public function __construct(Route $route)
108
    {
109
        // add relations to json api methods array
110
        $this->addRelationMethods();
111
        $actionName   = $route->getActionName();
112
        $calledMethod = substr($actionName, strpos($actionName, PhpInterface::AT) + 1);
113
        if ($this->jsonApi === false && in_array($calledMethod, $this->jsonApiMethods)) {
114
            Json::outputErrors(
115
                [
116
                    [
117
                        JSONApiInterface::ERROR_TITLE  => 'JSON API support disabled',
118
                        JSONApiInterface::ERROR_DETAIL => 'JSON API method ' . $calledMethod
119
                            .
120
                            ' was called. You can`t call this method while JSON API support is disabled.',
121
                    ],
122
                ]
123
            );
124
        }
125
126
        $this->setEntities();
127
        $this->setDefaults();
128
        $this->setConfigOptions($calledMethod);
129
    }
130
131
    /**
132
     * Responds with header of an allowed/available http methods
133
     * @return mixed
134
     */
135
    public function options()
136
    {
137
        // this seems like needless params passed by default, but they needed for backward compatibility in Laravel prev versions
138
        return response('', 200)->withHeaders([
139
            'Allow'                            => HTTPMethodsInterface::HTTP_METHODS_AVAILABLE,
140
            JSONApiInterface::CONTENT_TYPE_KEY => JSONApiInterface::HEADER_CONTENT_TYPE_VALUE,
141
        ]);
142
    }
143
144
    /**
145
     * GET Output all entries for this Entity with page/limit pagination support
146
     *
147
     * @param Request $request
148
     * @return IlluminateResponse
149
     * @throws \SoliDry\Exceptions\AttributesException
150
     */
151
    public function index(Request $request) : IlluminateResponse
152
    {
153
        $meta       = [];
154
        $sqlOptions = $this->setSqlOptions($request);
155
        if ($this->isTree === true) {
156
            $tree = $this->getAllTreeEntities($sqlOptions);
157
            $meta = [strtolower($this->entity) . PhpInterface::UNDERSCORE . JSONApiInterface::META_TREE => $tree->toArray()];
158
        }
159
160
        $this->setPagination(true);
161
        /** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $pages */
162
        if ($this->configOptions->isCached()) {
163
            $pages = $this->getCached($request, $sqlOptions);
164
        } else {
165
            $pages = $this->getEntities($sqlOptions);
166
        }
167
168
        if ($this->configOptions->isBitMask() === true) {
169
            $this->setFlagsIndex($pages);
170
        }
171
172
        return $this->response->setSqlOptions($sqlOptions)->get($pages, $meta);
173
    }
174
175
    /**
176
     * GET Output one entry determined by unique id as uri param
177
     *
178
     * @param Request $request
179
     * @param int|string $id
180
     * @return IlluminateResponse
181
     * @throws \SoliDry\Exceptions\AttributesException
182
     */
183
    public function view(Request $request, $id) : IlluminateResponse
184
    {
185
        $meta       = [];
186
        $sqlOptions = $this->setSqlOptions($request);
187
        $sqlOptions->setId($id);
188
        $data = $sqlOptions->getData();
189
190
        if ($this->isTree === true) {
191
            $tree = $this->getSubTreeEntities($sqlOptions, $id);
192
            $meta = [strtolower($this->entity) . PhpInterface::UNDERSCORE . JSONApiInterface::META_TREE => $tree];
193
        }
194
195
        if ($this->configOptions->isCached()) {
196
            $item = $this->getCached($request, $sqlOptions);
197
        } else {
198
            $item = $this->getEntity($id, $data);
199
        }
200
201
        if ($this->configOptions->isBitMask() === true) {
202
            $this->setFlagsView($item);
203
        }
204
205
        return $this->response->setSqlOptions($sqlOptions)->get($item, $meta);
206
    }
207
208
    /**
209
     * POST Creates one entry specified by all input fields in $request
210
     *
211
     * @param Request $request
212
     * @return IlluminateResponse
213
     * @throws \SoliDry\Exceptions\AttributesException
214
     */
215
    public function create(Request $request) : IlluminateResponse
216
    {
217
        $meta              = [];
218
        $json              = Json::decode($request->getContent());
219
        $jsonApiAttributes = Json::getAttributes($json);
220
221
        // FSM initial state check
222
        if ($this->configOptions->isStateMachine() === true) {
223
            $this->checkFsmCreate($jsonApiAttributes);
224
        }
225
226
        // spell check
227
        if ($this->configOptions->isSpellCheck() === true) {
228
            $meta = $this->spellCheck($jsonApiAttributes);
229
        }
230
231
        // fill in model
232
        foreach ($this->props as $k => $v) {
233
            // request fields should match FormRequest fields
234
            if (isset($jsonApiAttributes[$k])) {
235
                $this->model->$k = $jsonApiAttributes[$k];
236
            }
237
        }
238
239
        // set bit mask
240
        if ($this->configOptions->isBitMask() === true) {
241
            $this->setMaskCreate($jsonApiAttributes);
242
        }
243
        $this->model->save();
244
245
        // jwt
246
        if ($this->configOptions->getIsJwtAction() === true) {
247
            $this->createJwtUser(); // !!! model is overridden
248
        }
249
250
        // set bit mask from model -> response
251
        if ($this->configOptions->isBitMask() === true) {
252
            $this->model = $this->setFlagsCreate();
253
        }
254
255
        $this->setRelationships($json, $this->model->id);
256
257
        return $this->response->get($this->model, $meta);
258
    }
259
260
    /**
261
     * PATCH Updates one entry determined by unique id as uri param for specified fields in $request
262
     *
263
     * @param Request $request
264
     * @param int|string $id
265
     * @return IlluminateResponse
266
     * @throws \SoliDry\Exceptions\AttributesException
267
     */
268
    public function update(Request $request, $id) : IlluminateResponse
269
    {
270
        $meta = [];
271
272
        // get json raw input and parse attrs
273
        $json              = Json::decode($request->getContent());
274
        $jsonApiAttributes = Json::getAttributes($json);
275
        $model             = $this->getEntity($id);
276
277
        // FSM transition check
278
        if ($this->configOptions->isStateMachine() === true) {
279
            $this->checkFsmUpdate($jsonApiAttributes, $model);
280
        }
281
282
        // spell check
283
        if ($this->configOptions->isSpellCheck() === true) {
284
            $meta = $this->spellCheck($jsonApiAttributes);
285
        }
286
287
        $this->processUpdate($model, $jsonApiAttributes);
288
        $model->save();
289
290
        $this->setRelationships($json, $model->id, true);
291
292
        // set bit mask
293
        if ($this->configOptions->isBitMask() === true) {
294
            $this->setFlagsUpdate($model);
295
        }
296
297
        return $this->response->get($model, $meta);
298
    }
299
300
    /**
301
     * Process model update
302
     * @param $model
303
     * @param array $jsonApiAttributes
304
     * @throws \SoliDry\Exceptions\AttributesException
305
     */
306
    private function processUpdate($model, array $jsonApiAttributes)
307
    {
308
        // jwt
309
        $isJwtAction = $this->configOptions->getIsJwtAction();
310
        if ($isJwtAction === true && (bool)$jsonApiAttributes[JwtInterface::JWT] === true) {
311
            $this->updateJwtUser($model, $jsonApiAttributes);
312
        } else { // standard processing
313
            foreach ($this->props as $k => $v) {
314
                // request fields should match FormRequest fields
315
                if (empty($jsonApiAttributes[$k]) === false) {
316
                    if ($isJwtAction === true && $k === JwtInterface::PASSWORD) {// it is a regular query with password updated and jwt enabled - hash the password
317
                        $model->$k = password_hash($jsonApiAttributes[$k], PASSWORD_DEFAULT);
318
                    } else {
319
                        $model->$k = $jsonApiAttributes[$k];
320
                    }
321
                }
322
            }
323
        }
324
325
        // set bit mask
326
        if ($this->configOptions->isBitMask() === true) {
327
            $this->setMaskUpdate($model, $jsonApiAttributes);
328
        }
329
    }
330
331
    /**
332
     * DELETE Deletes one entry determined by unique id as uri param
333
     *
334
     * @param Request $request
335
     * @param int|string $id
336
     * @return IlluminateResponse
337
     */
338
    public function delete(Request $request, $id) : IlluminateResponse
339
    {
340
        $model = $this->getEntity($id);
341
        if ($model !== null) {
342
            $model->delete();
343
        }
344
345
        return $this->response->getResponse(Json::prepareSerializedData(new Collection()), JSONApiInterface::HTTP_RESPONSE_CODE_NO_CONTENT);
346
    }
347
348
    /**
349
     *  Adds {HTTPMethod}Relations to array of route methods
350
     */
351
    private function addRelationMethods()
352
    {
353
        $ucRelations            = ucfirst(JSONApiInterface::URI_METHOD_RELATIONS);
354
        $this->jsonApiMethods[] = JSONApiInterface::URI_METHOD_CREATE . $ucRelations;
355
        $this->jsonApiMethods[] = JSONApiInterface::URI_METHOD_UPDATE . $ucRelations;
356
        $this->jsonApiMethods[] = JSONApiInterface::URI_METHOD_DELETE . $ucRelations;
357
    }
358
}