Passed
Push — master ( 4fdfa9...0c2241 )
by Iman
04:38
created

ExecuteApi::fetchDataFromDB()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 7
dl 0
loc 14
rs 9.4285
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 CB;
8
use Illuminate\Support\Facades\Schema;
9
10
class ExecuteApi
11
{
12
    private $ctrl;
13
14
    /**
15
     * ExecuteApi constructor.
16
     *
17
     * @param $ctrl
18
     */
19
    public function __construct($ctrl)
20
    {
21
        $this->ctrl = $ctrl;
22
    }
23
24
    public function execute()
25
    {
26
        $result = [];
27
        $posts = request()->all();
28
        //$posts_keys = array_keys($posts);
29
        //$posts_values = array_values($posts);
30
31
        $row_api = DB::table('cms_apicustom')->where('permalink', $this->ctrl->permalink)->first();
0 ignored issues
show
Bug introduced by
The type crocodicstudio\crudboost...ollers\ApiController\DB was not found. Did you mean DB? If so, make sure to prefix the type with \.
Loading history...
32
33
        $actionType = $row_api->aksi;
34
        $table = $row_api->tabel;
35
36
        $debugModeMessage = 'You are in debug mode !';
37
38
        /* Do some custome pre-checking for posted data, if failed discard API execution */
39
        $this->doCustomePrecheck($posts, $result, $debugModeMessage);
40
41
        /* Method Type validation */
42
        $this->validateMethodType($row_api, $result, $debugModeMessage, $posts);
43
44
        /* Check the row is exists or not */
45
        $this->checkApiDefined($row_api, $result, $debugModeMessage, $posts);
46
47
        @$parameters = unserialize($row_api->parameters);
48
        @$responses = unserialize($row_api->responses);
49
50
        /*
51
        | ----------------------------------------------
52
        | User Data Validation
53
        | ----------------------------------------------
54
        |
55
        */
56
        list($type_except, $input_validator, $result) = $this->validateParams($parameters, $posts, $table, $result, $debugModeMessage);
57
58
        $responses_fields = $this->prepareResponses($responses);
59
60
        $this->ctrl->hookBefore($posts);
61
62
        $limit = ($posts['limit']) ?: 20;
63
        $offset = ($posts['offset']) ?: 0;
64
        $orderby = ($posts['orderby']) ?: $table.'.id,desc';
65
66
        unset($posts['limit'], $posts['offset'], $posts['orderby']);
67
68
        if (in_array($actionType, ['list', 'detail', 'delete'])) {
69
            $data = $this->fetchDataFromDB($table, $offset, $limit, $responses, $responses_fields, $parameters, $posts);
70
71
            $this->filterRows($data, $parameters, $posts, $table, $type_except);
72
73
            //IF SQL WHERE IS NOT NULL
74
            if ($row_api->sql_where) {
75
                $data->whereraw($row_api->sql_where);
76
            }
77
78
            $this->ctrl->hookQuery($data);
79
            if ($actionType == 'list') {
80
                $result = $this->handleListAction($table, $orderby, $data, $result, $debugModeMessage, $responses_fields);
81
            }
82
            $result = $this->handleDetailsAction($actionType, $result, $debugModeMessage, $data, $parameters, $posts, $responses_fields);
83
            if ($actionType == 'delete') {
84
                $result = $this->handleDeleteAction($actionType, $table, $data, $result, $debugModeMessage);
0 ignored issues
show
Unused Code introduced by
The call to crocodicstudio\crudboost...i::handleDeleteAction() has too many arguments starting with $debugModeMessage. ( Ignorable by Annotation )

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

84
                /** @scrutinizer ignore-call */ 
85
                $result = $this->handleDeleteAction($actionType, $table, $data, $result, $debugModeMessage);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
85
            }
86
        }
87
88
        if (in_array($actionType, ['save_add', 'save_edit'])) {
89
            $rowAssign = array_filter($input_validator, function ($column) use ($table) {
90
                return Schema::hasColumn($table, $column);
91
            }, ARRAY_FILTER_USE_KEY);
92
93
            $this->handleAddEdit($parameters, $posts, $rowAssign);
94
        }
95
96
        $this->show($result, $debugModeMessage, $posts);
97
    }
98
99
    /**
100
     * @param $result
101
     * @param $debugModeMessage
102
     * @param $posts
103
     * @return mixed
104
     */
105
    private function show($result, $debugModeMessage, $posts)
106
    {
107
        $result['api_status'] = $this->ctrl->hook_api_status ?: $result['api_status'];
108
        $result['api_message'] = $this->ctrl->hook_api_message ?: $result['api_message'];
109
110
        if (cbGetsetting('api_debug_mode') == 'true') {
111
            $result['api_authorization'] = $debugModeMessage;
112
        }
113
114
        $this->ctrl->hookAfter($posts, $result);
115
116
        sendAndTerminate(response()->json($result));
0 ignored issues
show
Bug introduced by
The method json() does not exist on Symfony\Component\HttpFoundation\Response. It seems like you code against a sub-type of Symfony\Component\HttpFoundation\Response such as Illuminate\Http\Response or Illuminate\Http\JsonResponse or Illuminate\Http\RedirectResponse. ( Ignorable by Annotation )

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

116
        sendAndTerminate(response()->/** @scrutinizer ignore-call */ json($result));
Loading history...
117
    }
118
119
    /**
120
     * @param $responses
121
     * @return array
122
     */
123
    private function prepareResponses($responses)
124
    {
125
        $responsesFields = [];
126
        foreach ($responses as $r) {
127
            if ($r['used']) {
128
                $responsesFields[] = $r['name'];
129
            }
130
        }
131
132
        return $responsesFields;
133
    }
134
135
    /**
136
     * @param $table
137
     * @param $orderby
138
     * @param $data
139
     * @param $result
140
     * @param $debugModeMessage
141
     * @param $responsesFields
142
     * @return array
143
     */
144
    private function handleListAction($table, $orderby, $data, $result, $debugModeMessage, $responsesFields)
145
    {
146
        $orderbyCol = $table.'.id';
147
        $orderbyVal = 'desc';
148
149
        if ($orderby) {
150
            list($orderbyCol, $orderbyVal) = explode(',', $orderby);
151
        }
152
153
        $rows = $data->orderby($orderbyCol, $orderbyVal)->get();
154
155
        $result['api_status'] = 0;
156
        $result['api_message'] = 'There is no data found !';
157
        if (cbGetsetting('api_debug_mode') == 'true') {
158
            $result['api_authorization'] = $debugModeMessage;
159
        }
160
        $result['data'] = [];
161
        if ($rows) {
162
            $result = $this->handleRows($result, $debugModeMessage, $responsesFields, $rows);
163
        }
164
165
        return $result;
166
    }
167
168
    /**
169
     * @param $table
170
     * @param $data
171
     * @param $result
172
     * @param $debugModeMessage
173
     * @return mixed
174
     */
175
    private function handleDeleteAction($table, $data, $result, $debugModeMessage)
176
    {
177
        if (\Schema::hasColumn($table, 'deleted_at')) {
178
            $delete = $data->update(['deleted_at' => date('Y-m-d H:i:s')]);
179
        } else {
180
            $delete = $data->delete();
181
        }
182
183
        $result['api_status'] = ($delete) ? 1 : 0;
184
        $result['api_message'] = ($delete) ? "success" : "failed";
185
        if (cbGetsetting('api_debug_mode') == 'true') {
186
            $result['api_authorization'] = $debugModeMessage;
187
        }
188
189
        return $result;
190
    }
191
192
    /**
193
     * @param $data
194
     * @param $parameters
195
     * @param $posts
196
     * @param $table
197
     * @param $typeExcept
198
     */
199
    private function filterRows($data, $parameters, $posts, $table, $typeExcept)
200
    {
201
        $data->where(function ($w) use ($parameters, $posts, $table, $typeExcept) {
202
            foreach ($parameters as $param) {
203
                $name = $param['name'];
204
                $type = $param['type'];
205
                $value = $posts[$name];
206
                $used = $param['used'];
207
                $required = $param['required'];
208
209
                if (in_array($type, $typeExcept)) {
210
                    continue;
211
                }
212
213
                if ($param['config'] != '' && substr($param['config'], 0, 1) != '*') {
214
                    $value = $param['config'];
215
                }
216
217
                if ($required == '1') {
218
                    $this->applyWhere($w, $table, $name, $value);
219
                } else {
220
                    if ($used && $value) {
221
                        $this->applyWhere($w, $table, $name, $value);
222
                    }
223
                }
224
            }
225
        });
226
    }
227
228
    /**
229
     * @param $w
230
     * @param $table
231
     * @param $name
232
     * @param $value
233
     */
234
    private function applyWhere($w, $table, $name, $value)
235
    {
236
        if (\Schema::hasColumn($table, $name)) {
237
            $w->where($table.'.'.$name, $value);
238
        } else {
239
            $w->having($name, '=', $value);
240
        }
241
    }
242
243
    /**
244
     * @param $parameters
245
     * @param $posts
246
     * @param $data
247
     * @param $table
248
     * @return null
249
     */
250
    private function params($parameters, $posts, $data, $table)
251
    {
252
        foreach ($parameters as $param) {
253
            $name = $param['name'];
254
            $type = $param['type'];
255
            $value = $posts[$name];
256
            $used = $param['used'];
257
            $required = $param['required'];
258
            $config = $param['config'];
259
260
            if ($type == 'password') {
261
                $data->addselect($table.'.'.$name);
262
            }
263
264
            if ($type !== 'search') {
265
                continue;
266
            }
267
            $search_in = explode(',', $config);
268
269
            if ($required == '1' || ($used && $value)) {
270
                $this->applyLike($data, $search_in, $value);
271
            }
272
        }
273
    }
274
275
    /**
276
     * @param $result
277
     * @param $debugModeMessage
278
     * @param $responsesFields
279
     * @param $rows
280
     * @return array
281
     */
282
    private function handleRows($result, $debugModeMessage, $responsesFields, $rows)
283
    {
284
        $uploadsFormatCandidate = explode(',', cbConfig("UPLOAD_TYPES"));
285
        foreach ($rows as &$row) {
286
            foreach ($row as $k => $v) {
287
                $ext = \File::extension($v);
288
                if (in_array($ext, $uploadsFormatCandidate)) {
289
                    $row->$k = asset($v);
290
                }
291
292
                if (! in_array($k, $responsesFields)) {
293
                    unset($row[$k]);
294
                }
295
            }
296
        }
297
298
        $result['api_status'] = 1;
299
        $result['api_message'] = 'success';
300
        if (cbGetsetting('api_debug_mode') == 'true') {
301
            $result['api_authorization'] = $debugModeMessage;
302
        }
303
        $result['data'] = $rows;
304
305
        return $result;
306
    }
307
308
    /**
309
     * @param $parameters
310
     * @param $posts
311
     * @param $rowAssign
312
     */
313
    private function handleAddEdit($parameters, $posts, $rowAssign)
314
    {
315
        foreach ($parameters as $param) {
316
            $name = $param['name'];
317
            $used = $param['used'];
318
            $value = $posts[$name];
319
            if ($used == '1' && $value == '') {
320
                unset($rowAssign[$name]);
321
            }
322
        }
323
    }
324
325
    /**
326
     * @param $result
327
     * @param $debugModeMessage
328
     * @param $posts
329
     * @return mixed
330
     */
331
    private function passwordError($result, $debugModeMessage, $posts)
332
    {
333
        $result['api_status'] = 0;
334
        $result['api_message'] = cbTrans('alert_password_wrong');
335
        if (cbGetsetting('api_debug_mode') == 'true') {
336
            $result['api_authorization'] = $debugModeMessage;
337
        }
338
339
        $this->show($result, $debugModeMessage, $posts);
340
    }
341
342
    /**
343
     * @param $rows
344
     * @param $responsesFields
345
     * @param $row
346
     */
347
    private function handleFile($rows, $responsesFields, $row)
348
    {
349
        foreach ($rows as $k => $v) {
350
            $ext = \File::extension($v);
351
            if (FieldDetector::isUploadField($ext)) {
352
                $rows->$k = asset($v);
353
            }
354
355
            if (! in_array($k, $responsesFields)) {
356
                unset($row[$k]);
357
            }
358
        }
359
    }
360
361
    /**
362
     * @param $table
363
     * @param $data
364
     * @param $responses
365
     *
366
     * @param $responsesFields
367
     * @return array
368
     */
369
    private function responses($table, $data, $responses, $responsesFields)
370
    {
371
        $name_tmp = [];
372
373
        $responses = $this->filterRedundantResp($responses);
374
375
        foreach ($responses as $resp) {
376
            $name = $resp['name'];
377
            $subquery = $resp['subquery'];
378
            $used = intval($resp['used']);
379
380
            if (in_array($name, $name_tmp)) {
381
                continue;
382
            }
383
384
            if ($subquery) {
385
                $data->addSelect(DB::raw('('.$subquery.') as '.$name));
386
                $name_tmp[] = $name;
387
                continue;
388
            }
389
390
            if ($used) {
391
                $data->addSelect($table.'.'.$name);
392
            }
393
394
            $name_tmp[] = $name;
395
            $name_tmp = $this->joinRelatedTables($table, $responsesFields, $name, $data, $name_tmp);
396
        }
397
398
        return $data;
399
    }
400
401
    /**
402
     * @param $result
403
     * @param $debugModeMessage
404
     * @param $rows
405
     * @return array
406
     */
407
    private function success($result, $debugModeMessage, $rows)
408
    {
409
        $result['api_status'] = 1;
410
        $result['api_message'] = 'success';
411
        if (cbGetsetting('api_debug_mode') == 'true') {
412
            $result['api_authorization'] = $debugModeMessage;
413
        }
414
        $rows = (array) $rows;
415
        $result = array_merge($result, $rows);
416
417
        return $result;
418
    }
419
420
    /**
421
     * @param $data
422
     * @param $search_in
423
     * @param $value
424
     */
425
    private function applyLike($data, $search_in, $value)
426
    {
427
        $data->where(function ($w) use ($search_in, $value) {
428
            foreach ($search_in as $k => $field) {
429
                if ($k == 0) {
430
                    $w->where($field, "like", "%$value%");
431
                } else {
432
                    $w->orWhere($field, "like", "%$value%");
433
                }
434
            }
435
        });
436
    }
437
438
    /**
439
     * @param $table
440
     * @param $responsesFields
441
     * @param $name
442
     * @param $data
443
     * @param $nameTmp
444
     * @return array
445
     */
446
    private function joinRelatedTables($table, $responsesFields, $name, $data, $nameTmp)
447
    {
448
        if (! DbInspector::isForeignKey($name)) {
449
            return $nameTmp;
450
        }
451
        $jointable = DbInspector::getTableForeignKey($name);
452
        $data->leftjoin($jointable, $jointable.'.id', '=', $table.'.'.$name);
453
        foreach (\Schema::getColumnListing($jointable) as $jf) {
454
            $jfAlias = $jointable.'_'.$jf;
455
            if (in_array($jfAlias, $responsesFields)) {
456
                $data->addselect($jointable.'.'.$jf.' as '.$jfAlias);
457
                $nameTmp[] = $jfAlias;
458
            }
459
        }
460
        return $nameTmp;
461
    }
462
463
    /**
464
     * @param $rowApi
465
     * @param $result
466
     * @param $debugModeMessage
467
     * @param $posts
468
     * @return mixed
469
     */
470
    private function validateMethodType($rowApi, $result, $debugModeMessage, $posts)
471
    {
472
        $methodType = $rowApi->method_type;
473
        if ($methodType && Request::isMethod($methodType)) {
0 ignored issues
show
Bug introduced by
The type crocodicstudio\crudboost...s\ApiController\Request was not found. Did you mean Request? If so, make sure to prefix the type with \.
Loading history...
474
            return true;
475
        }
476
        $result['api_status'] = 0;
477
        $result['api_message'] = "The request method is not allowed !";
478
479
        $this->show($result, $debugModeMessage, $posts);
480
    }
481
482
    /**
483
     * @param $posts
484
     * @param $result
485
     * @param $debugModeMessage
486
     * @return mixed
487
     */
488
    private function doCustomePrecheck($posts, $result, $debugModeMessage)
489
    {
490
        $this->ctrl->hookValidate($posts);
491
        if ($this->ctrl->validate) { // hook have to return true
492
            $result['api_status'] = 0;
493
            $result['api_message'] = "Failed to execute API !";
494
495
            $this->show($result, $debugModeMessage, $posts);
496
        }
497
498
        return $result;
499
    }
500
501
    /**
502
     * @param $row_api
503
     * @param $result
504
     * @param $debugModeMessage
505
     * @param $posts
506
     * @return mixed
507
     */
508
    private function checkApiDefined($row_api, $result, $debugModeMessage, $posts)
509
    {
510
        if (! $row_api) {
511
            $result['api_status'] = 0;
512
            $result['api_message'] = 'Sorry this API is no longer available, maybe has changed by admin, or please make sure api url is correct.';
513
514
            $this->show($result, $debugModeMessage, $posts);
515
        }
516
517
        return $result;
518
    }
519
520
    /**
521
     * @param $input_validator
522
     * @param $data_validation
523
     * @param $result
524
     * @param $debugModeMessage
525
     * @param $posts
526
     * @return mixed
527
     */
528
    private function doValidation($input_validator, $data_validation, $result, $debugModeMessage, $posts)
529
    {
530
        $validator = Validator::make($input_validator, $data_validation);
0 ignored issues
show
Bug introduced by
The type crocodicstudio\crudboost...ApiController\Validator was not found. Did you mean Validator? If so, make sure to prefix the type with \.
Loading history...
531
        if (! $validator->fails()) {
532
            return $result;
533
        }
534
        $message = $validator->errors()->all();
535
        $message = implode(', ', $message);
536
        $result['api_status'] = 0;
537
        $result['api_message'] = $message;
538
539
        $this->show($result, $debugModeMessage, $posts);
540
    }
541
542
    /**
543
     * @param $action_type
544
     * @param $result
545
     * @param $debugModeMessage
546
     * @param $data
547
     * @param $parameters
548
     * @param $posts
549
     * @param $responses_fields
550
     * @return array
551
     */
552
    private function handleDetailsAction($action_type, $result, $debugModeMessage, $data, $parameters, $posts, $responses_fields)
553
    {
554
        if ($action_type != 'detail') {
555
            return $result;
556
        }
557
558
        $result['api_status'] = 0;
559
        $result['api_message'] = 'There is no data found !';
560
561
        if (cbGetsetting('api_debug_mode') == 'true') {
562
            $result['api_authorization'] = $debugModeMessage;
563
        }
564
565
        $row = $data->first();
566
567
        if ($row) {
568
            foreach ($parameters as $param) {
569
                $name = $param['name'];
570
                $type = $param['type'];
571
                $value = $posts[$name];
572
                $used = $param['used'];
573
                $required = $param['required'];
574
575
                if ($param['config'] != '' && substr($param['config'], 0, 1) != '*') {
576
                    $value = $param['config'];
577
                }
578
579
                if ($required && $type == 'password' && ! Hash::check($value, $row->{$name})) {
0 ignored issues
show
Bug introduced by
The type crocodicstudio\crudboost...lers\ApiController\Hash was not found. Did you mean Hash? If so, make sure to prefix the type with \.
Loading history...
580
                    $this->passwordError($result, $debugModeMessage, $posts);
581
                }
582
583
                if (! $required && $used && $value && ! Hash::check($value, $row->{$name})) {
584
                    $this->passwordError($result, $debugModeMessage, $posts);
585
                }
586
            }
587
588
            $this->handleFile($row, $responses_fields, $row);
589
590
            $result = $this->success($result, $debugModeMessage, $row);
591
        }
592
        return $result;
593
    }
594
595
    /**
596
     * @param $responses
597
     * @return array
598
     */
599
    private function filterRedundantResp($responses)
600
    {
601
        $responses = array_filter($responses, function ($resp) {
602
            return ! ($resp['name'] == 'ref_id' || $resp['type'] == 'custom');
603
        });
604
605
        $responses = array_filter($responses, function ($resp) {
606
            return (intval($resp['used']) != 0 || DbInspector::isForeignKey($resp['name']));
607
        });
608
609
        return $responses;
610
    }
611
612
    /**
613
     * @param $parameters
614
     * @param $posts
615
     * @param $table
616
     * @param $result
617
     * @param $debugModeMessage
618
     * @return array
619
     */
620
    private function validateParams($parameters, $posts, $table, $result, $debugModeMessage)
621
    {
622
        if (!$parameters) {
623
            return ['', '', $result];
624
        }
625
        $type_except = ['password', 'ref', 'base64_file', 'custom', 'search'];
626
        $inputValidator = [];
627
        $dataValidation = [];
628
629
        foreach ($parameters as $param) {
630
            $name = $param['name'];
631
            $value = $posts[$name];
632
            $used = $param['used'];
633
634
            if ($used == 0) {
635
                continue;
636
            }
637
            if ($param['config'] && substr($param['config'], 0, 1) != '*') {
638
                continue;
639
            }
640
641
            $inputValidator[$name] = $value;
642
            $dataValidation[$name] = app(ValidationRules::class)->make($param, $type_except, $table);
643
        }
644
645
        $result = $this->doValidation($inputValidator, $dataValidation, $result, $debugModeMessage, $posts);
646
647
        return [$type_except, $inputValidator, $result];
648
    }
649
650
    /**
651
     * @param $table
652
     * @param $offset
653
     * @param $limit
654
     * @param $responses
655
     * @param $responses_fields
656
     * @param $parameters
657
     * @param $posts
658
     * @return array
659
     */
660
    private function fetchDataFromDB($table, $offset, $limit, $responses, $responses_fields, $parameters, $posts)
661
    {
662
        $data = DB::table($table);
663
        $data->skip($offset);
664
        $data->take($limit);
665
        $data = $this->responses($table, $data, $responses, $responses_fields); //End Responses
666
667
        $this->params($parameters, $posts, $data, $table);
668
669
        if (\Schema::hasColumn($table, 'deleted_at')) {
670
            $data->where($table.'.deleted_at', null);
671
        }
672
673
        return $data;
674
    }
675
}