Passed
Push — master ( 2ff9b2...70d4c6 )
by Iman
03:58
created

ExecuteApi::checkApiDefined()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace crocodicstudio\crudbooster\controllers\ApiController;
4
5
use crocodicstudio\crudbooster\helpers\DbInspector;
6
use crocodicstudio\crudbooster\Modules\ModuleGenerator\ControllerGenerator\FieldDetector;
7
use Illuminate\Support\Facades\DB;
8
use Illuminate\Support\Facades\Hash;
9
use Illuminate\Support\Facades\Schema;
10
use Illuminate\Support\Facades\Validator;
11
12
class ExecuteApi
13
{
14
    private $ctrl;
15
16
    /**
17
     * ExecuteApi constructor.
18
     *
19
     * @param $ctrl
20
     */
21
    public function __construct($ctrl)
22
    {
23
        $this->ctrl = $ctrl;
24
    }
25
26
    public function execute()
27
    {
28
        $rowApi = DB::table('cms_apicustom')->where('permalink', $this->ctrl->permalink)->first();
29
        ApiValidations::doValidations($rowApi, $this->ctrl);
30
31
        $table = $rowApi->tabel;
32
33
        @$parameters = unserialize($rowApi->parameters);
34
        list($type_except, $input_validator) = $this->validateParams($parameters, $table);
35
36
        $posts = request()->all();
37
        $this->ctrl->hookBefore($posts);
38
39
40
        unset($posts['limit'], $posts['offset'], $posts['orderby']);
41
        $actionType = $rowApi->aksi;
42
        if (in_array($actionType, ['list', 'detail', 'delete'])) {
43
            @$responses = unserialize($rowApi->responses);
44
            $responses_fields = $this->prepareResponses($responses);
45
            $data = $this->fetchDataFromDB($table, $responses, $responses_fields, $parameters, $posts);
46
47
            $this->filterRows($data, $parameters, $posts, $table, $type_except);
48
49
            if (!is_null($rowApi->sql_where)) {
50
                $data->whereraw($rowApi->sql_where);
51
            }
52
53
            $this->ctrl->hookQuery($data);
54
            $result = [];
55
            if ($actionType == 'list') {
56
                $result = $this->handleListAction($table, $data, $responses_fields);
57
            } elseif ($actionType == 'detail') {
58
                $result = $this->handleDetailsAction($data, $parameters, $posts, $responses_fields);
59
            } elseif ($actionType == 'delete') {
60
                $result = $this->handleDeleteAction($table, $data);
61
            }
62
            ApiResponder::send($result, $posts, $this->ctrl);
63
        } elseif (in_array($actionType, ['save_add', 'save_edit'])) {
64
            $rowAssign = array_filter($input_validator, function ($column) use ($table) {
65
                return Schema::hasColumn($table, $column);
66
            }, ARRAY_FILTER_USE_KEY);
67
68
            $this->handleAddEdit($parameters, $posts, $rowAssign);
69
        }
70
71
    }
72
73
    /**
74
     * @param $responses
75
     * @return array
76
     */
77
    private function prepareResponses($responses)
78
    {
79
        $responsesFields = [];
80
        foreach ($responses as $r) {
81
            if ($r['used']) {
82
                $responsesFields[] = $r['name'];
83
            }
84
        }
85
86
        return $responsesFields;
87
    }
88
89
    /**
90
     * @param $table
91
     * @param $data
92
     * @param $responsesFields
93
     * @return array
94
     */
95
    private function handleListAction($table, $data, $responsesFields)
96
    {
97
        $rows = $this->sortRows($table, $data);
98
        if ($rows) {
99
            return $this->handleRows($responsesFields, $rows);
100
        }
101
        $result = ApiResponder::makeResult(0, 'No data found !');
102
        $result['data'] = [];
103
        ApiResponder::send($result, request()->all(), $this->ctrl);
104
    }
105
106
    /**
107
     * @param $table
108
     * @param $data
109
     * @return mixed
110
     */
111
    private function handleDeleteAction($table, $data)
112
    {
113
        if (\Schema::hasColumn($table, 'deleted_at')) {
114
            $delete = $data->update(['deleted_at' => date('Y-m-d H:i:s')]);
115
        } else {
116
            $delete = $data->delete();
117
        }
118
119
        $status = ($delete) ? 1 : 0;
120
        $msg = ($delete) ? "success" : "failed";
121
122
        return ApiResponder::makeResult($status, $msg);
123
    }
124
125
    /**
126
     * @param $data
127
     * @param $parameters
128
     * @param $posts
129
     * @param $table
130
     * @param $typeExcept
131
     */
132
    private function filterRows($data, $parameters, $posts, $table, $typeExcept)
133
    {
134
        $data->where(function ($w) use ($parameters, $posts, $table, $typeExcept) {
135
            foreach ($parameters as $param) {
136
                $name = $param['name'];
137
                $type = $param['type'];
138
                $value = $posts[$name];
139
                $used = $param['used'];
140
                $required = $param['required'];
141
142
                if (in_array($type, $typeExcept)) {
143
                    continue;
144
                }
145
146
                if ($param['config'] != '' && substr($param['config'], 0, 1) != '*') {
147
                    $value = $param['config'];
148
                }
149
150
                if ($required == '1') {
151
                    $this->applyWhere($w, $table, $name, $value);
152
                } else {
153
                    if ($used && $value) {
154
                        $this->applyWhere($w, $table, $name, $value);
155
                    }
156
                }
157
            }
158
        });
159
    }
160
161
    /**
162
     * @param $w
163
     * @param $table
164
     * @param $name
165
     * @param $value
166
     */
167
    private function applyWhere($w, $table, $name, $value)
168
    {
169
        if (\Schema::hasColumn($table, $name)) {
170
            $w->where($table.'.'.$name, $value);
171
        } else {
172
            $w->having($name, '=', $value);
173
        }
174
    }
175
176
    /**
177
     * @param $parameters
178
     * @param $posts
179
     * @param $data
180
     * @param $table
181
     * @return null
182
     */
183
    private function params($parameters, $posts, $data, $table)
184
    {
185
        foreach ($parameters as $param) {
186
            $name = $param['name'];
187
            $type = $param['type'];
188
            $value = $posts[$name];
189
            $used = $param['used'];
190
            $required = $param['required'];
191
            $config = $param['config'];
192
193
            if ($type == 'password') {
194
                $data->addselect($table.'.'.$name);
195
            }
196
197
            if ($type !== 'search') {
198
                continue;
199
            }
200
            $search_in = explode(',', $config);
201
202
            if ($required == '1' || ($used && $value)) {
203
                $this->applyLike($data, $search_in, $value);
204
            }
205
        }
206
    }
207
208
    /**
209
     * @param $responsesFields
210
     * @param $rows
211
     * @return array
212
     */
213
    private function handleRows($responsesFields, $rows)
214
    {
215
        foreach ($rows as &$row) {
216
            $this->handleFile($row, $responsesFields);
217
        }
218
219
        $result = ApiResponder::makeResult(1, 'success');
220
        $result['data'] = $rows;
221
222
        return $result;
223
    }
224
225
    /**
226
     * @param $parameters
227
     * @param $posts
228
     * @param $rowAssign
229
     */
230
    private function handleAddEdit($parameters, $posts, $rowAssign)
231
    {
232
        foreach ($parameters as $param) {
233
            $name = $param['name'];
234
            $used = $param['used'];
235
            $value = $posts[$name];
236
            if ($used == '1' && $value == '') {
237
                unset($rowAssign[$name]);
238
            }
239
        }
240
    }
241
242
    /**
243
     * @param $posts
244
     * @return mixed
245
     */
246
    private function passwordError($posts)
247
    {
248
        $result = ApiResponder::makeResult(0, cbTrans('alert_password_wrong'));
249
250
        ApiResponder::send($result, $posts, $this->ctrl);
251
    }
252
253
    /**
254
     * @param $rows
255
     * @param $responsesFields
256
     */
257
    private function handleFile($rows, $responsesFields)
258
    {
259
        foreach ($rows as $k => $v) {
260
            if (FieldDetector::isUploadField(\File::extension($v))) {
261
                $rows->$k = asset($v);
262
            }
263
264
            if (! in_array($k, $responsesFields)) {
265
                unset($rows->$k);
266
            }
267
        }
268
    }
269
270
    /**
271
     * @param $table
272
     * @param $data
273
     * @param $responses
274
     *
275
     * @param $responsesFields
276
     * @return array
277
     */
278
    private function responses($table, $data, $responses, $responsesFields)
279
    {
280
        $name_tmp = [];
281
282
        $responses = $this->filterRedundantResp($responses);
283
284
        foreach ($responses as $resp) {
285
            $name = $resp['name'];
286
            $subquery = $resp['subquery'];
287
            $used = intval($resp['used']);
288
289
            if (in_array($name, $name_tmp)) {
290
                continue;
291
            }
292
293
            if ($subquery) {
294
                $data->addSelect(DB::raw('('.$subquery.') as '.$name));
295
                $name_tmp[] = $name;
296
                continue;
297
            }
298
299
            if ($used) {
300
                $data->addSelect($table.'.'.$name);
301
            }
302
303
            $name_tmp[] = $name;
304
            $name_tmp = $this->joinRelatedTables($table, $responsesFields, $name, $data, $name_tmp);
305
        }
306
307
        return $data;
308
    }
309
310
    /**
311
     * @param $rows
312
     * @return array
313
     */
314
    private function success($rows)
315
    {
316
        $result = ApiResponder::makeResult(1, 'success');
317
318
        return array_merge($result, (array)$rows);
319
    }
320
321
    /**
322
     * @param $data
323
     * @param $search_in
324
     * @param $value
325
     */
326
    private function applyLike($data, $search_in, $value)
327
    {
328
        $data->where(function ($w) use ($search_in, $value) {
329
            foreach ($search_in as $k => $field) {
330
                $method = 'orWhere';
331
                if ($k == 0) {
332
                    $method = 'where';
333
                }
334
                $w->$method($field, "like", "%$value%");
335
            }
336
        });
337
    }
338
339
    /**
340
     * @param $table
341
     * @param $responsesFields
342
     * @param $name
343
     * @param $data
344
     * @param $nameTmp
345
     * @return array
346
     */
347
    private function joinRelatedTables($table, $responsesFields, $name, $data, $nameTmp)
348
    {
349
        if (! DbInspector::isForeignKey($name)) {
350
            return $nameTmp;
351
        }
352
        $joinTable = DbInspector::getTableForeignKey($name);
353
        $data->leftjoin($joinTable, $joinTable.'.id', '=', $table.'.'.$name);
354
        foreach (\Schema::getColumnListing($joinTable) as $jf) {
355
            $jfAlias = $joinTable.'_'.$jf;
356
            if (in_array($jfAlias, $responsesFields)) {
357
                $data->addselect($joinTable.'.'.$jf.' as '.$jfAlias);
358
                $nameTmp[] = $jfAlias;
359
            }
360
        }
361
362
        return $nameTmp;
363
    }
364
365
    /**
366
     * @param $inputValidator
367
     * @param $dataValidation
368
     * @param $posts
369
     * @return mixed
370
     */
371
    private function doValidation($inputValidator, $dataValidation, $posts)
372
    {
373
        $validator = Validator::make($inputValidator, $dataValidation);
374
        if (! $validator->fails()) {
375
            return true;
376
        }
377
        $message = $validator->errors()->all();
378
        $message = implode(', ', $message);
379
        $result = ApiResponder::makeResult(0, $message);
380
381
        ApiResponder::send($result, $posts, $this->ctrl);
382
    }
383
384
    /**
385
     * @param $data
386
     * @param $parameters
387
     * @param $posts
388
     * @param $responsesFields
389
     * @return array
390
     */
391
    private function handleDetailsAction($data, $parameters, $posts, $responsesFields)
392
    {
393
        $row = $data->first();
394
395
        if (! $row) {
396
            return ApiResponder::makeResult(0, 'There is no data found !');
397
        }
398
399
        foreach ($parameters as $param) {
400
            $name = $param['name'];
401
            $type = $param['type'];
402
            $value = $posts[$name];
403
            $used = $param['used'];
404
            $required = $param['required'];
405
406
            if ($param['config'] != '' && substr($param['config'], 0, 1) != '*') {
407
                $value = $param['config'];
408
            }
409
            if (Hash::check($value, $row->{$name})) {
410
                continue;
411
            }
412
413
            if ($required && $type == 'password') {
414
                $this->passwordError($posts);
415
            }
416
417
            if (! $required && $used && $value) {
418
                $this->passwordError($posts);
419
            }
420
        }
421
422
        $this->handleFile($row, $responsesFields);
423
424
        return $this->success($row);
425
    }
426
427
    /**
428
     * @param $responses
429
     * @return array
430
     */
431
    private function filterRedundantResp($responses)
432
    {
433
        $responses = array_filter($responses, function ($resp) {
434
            return ! ($resp['name'] == 'ref_id' || $resp['type'] == 'custom');
435
        });
436
437
        $responses = array_filter($responses, function ($resp) {
438
            return (intval($resp['used']) != 0 || DbInspector::isForeignKey($resp['name']));
439
        });
440
441
        return $responses;
442
    }
443
444
    /**
445
     * @param $parameters
446
     * @param $table
447
     * @return array
448
     */
449
    private function validateParams($parameters, $table)
450
    {
451
        $posts = request()->all();
452
        if (! $parameters) {
453
            return ['', ''];
454
        }
455
        $typeExcept = ['password', 'ref', 'base64_file', 'custom', 'search'];
456
        $inputValidator = [];
457
        $dataValidation = [];
458
459
        $parameters = array_filter($parameters, function ($param){
460
            return !(is_string($param['config'])&& !starts_with($param['config'], '*'));
461
        });
462
463
        foreach ($parameters as $param) {
464
            $name = $param['name'];
465
            $value = $posts[$name];
466
            $used = $param['used'];
467
468
            if ($used == 0) {
469
                continue;
470
            }
471
472
            $inputValidator[$name] = $value;
473
            $dataValidation[$name] = app(ValidationRules::class)->make($param, $typeExcept, $table);
474
        }
475
476
        $this->doValidation($inputValidator, $dataValidation, $posts);
477
478
        return [$typeExcept, $inputValidator];
479
    }
480
481
    /**
482
     * @param $table
483
     * @param $responses
484
     * @param $responsesFields
485
     * @param $parameters
486
     * @param $posts
487
     * @return array
488
     */
489
    private function fetchDataFromDB($table, $responses, $responsesFields, $parameters, $posts)
490
    {
491
        $data = DB::table($table);
492
        $data->skip(request('offset', 0));
0 ignored issues
show
Bug introduced by
request('offset', 0) of type Illuminate\Http\Request|string|array is incompatible with the type integer expected by parameter $value of Illuminate\Database\Query\Builder::skip(). ( Ignorable by Annotation )

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

492
        $data->skip(/** @scrutinizer ignore-type */ request('offset', 0));
Loading history...
493
        $data->take(request('limit', 20));
0 ignored issues
show
Bug introduced by
request('limit', 20) of type Illuminate\Http\Request|string|array is incompatible with the type integer expected by parameter $value of Illuminate\Database\Query\Builder::take(). ( Ignorable by Annotation )

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

493
        $data->take(/** @scrutinizer ignore-type */ request('limit', 20));
Loading history...
494
        $data = $this->responses($table, $data, $responses, $responsesFields); //End Responses
495
496
        $this->params($parameters, $posts, $data, $table);
497
498
        if (\Schema::hasColumn($table, 'deleted_at')) {
499
            $data->where($table.'.deleted_at', null);
500
        }
501
502
        return $data;
503
    }
504
505
    /**
506
     * @param $table
507
     * @param $data
508
     * @return mixed
509
     */
510
    private function sortRows($table, $data)
511
    {
512
        $orderBy = request('orderby', $table.'.id,desc');
513
514
        list($orderByCol, $orderByVal) = explode(',', $orderBy);
0 ignored issues
show
Bug introduced by
It seems like $orderBy can also be of type array; however, parameter $string of explode() does only seem to accept string, 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

514
        list($orderByCol, $orderByVal) = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
515
516
        $rows = $data->orderby($orderByCol, $orderByVal)->get();
517
518
        return $rows;
519
    }
520
}