Completed
Push — master ( f1f480...26274e )
by Arthur
17s queued 10s
created

ApiController::getResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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