Passed
Push — master ( ebd448...4a70a7 )
by Iman
05:44
created

ExecuteApi::makeResult()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 3
dl 0
loc 10
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
27
        //$posts_keys = array_keys($posts);
28
        //$posts_values = array_values($posts);
29
30
        $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...
31
32
        $actionType = $row_api->aksi;
33
        $table = $row_api->tabel;
34
35
        $debugModeMessage = 'You are in debug mode !';
36
37
        /* Do some custome pre-checking for posted data, if failed discard API execution */
38
        $this->doCustomePrecheck($debugModeMessage);
39
40
        /* Method Type validation */
41
        $methodType = $row_api->method_type;
42
        $this->validateMethodType($methodType, $debugModeMessage);
43
44
        /* Check the row is exists or not */
45
        $this->checkApiDefined($row_api, $debugModeMessage);
46
        @$parameters = unserialize($row_api->parameters);
47
        @$responses = unserialize($row_api->responses);
48
49
        /*
50
        | ----------------------------------------------
51
        | User Data Validation
52
        | ----------------------------------------------
53
        |
54
        */
55
        $posts = request()->all();
56
        list($type_except, $input_validator) = $this->validateParams($parameters, $posts, $table, $debugModeMessage);
57
58
59
        $responses_fields = $this->prepareResponses($responses);
60
61
        $this->ctrl->hookBefore($posts);
62
63
        $limit = ($posts['limit']) ?: 20;
64
        $offset = ($posts['offset']) ?: 0;
65
        $orderby = ($posts['orderby']) ?: $table.'.id,desc';
66
67
        unset($posts['limit'], $posts['offset'], $posts['orderby']);
68
69
        if (in_array($actionType, ['list', 'detail', 'delete'])) {
70
            $data = $this->fetchDataFromDB($table, $offset, $limit, $responses, $responses_fields, $parameters, $posts);
71
72
            $this->filterRows($data, $parameters, $posts, $table, $type_except);
73
74
            //IF SQL WHERE IS NOT NULL
75
            if ($row_api->sql_where) {
76
                $data->whereraw($row_api->sql_where);
77
            }
78
79
            $this->ctrl->hookQuery($data);
80
81
            $result = [];
82
            if ($actionType == 'list') {
83
                $result = $this->handleListAction($table, $orderby, $data, $debugModeMessage, $responses_fields);
84
            }elseif ($actionType == 'detail') {
85
                $result = $this->handleDetailsAction($debugModeMessage, $data, $parameters, $posts, $responses_fields);
86
            }elseif ($actionType == 'delete') {
87
                $result = $this->handleDeleteAction($table, $data, $debugModeMessage);
88
            }
89
        }elseif (in_array($actionType, ['save_add', 'save_edit'])) {
90
            $rowAssign = array_filter($input_validator, function ($column) use ($table) {
91
                return Schema::hasColumn($table, $column);
92
            }, ARRAY_FILTER_USE_KEY);
93
94
            $this->handleAddEdit($parameters, $posts, $rowAssign);
95
        }
96
97
        $this->show($result, $debugModeMessage, $posts);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
98
    }
99
100
    /**
101
     * @param $result
102
     * @param $debugModeMessage
103
     * @param $posts
104
     * @return mixed
105
     */
106
    private function show($result, $debugModeMessage, $posts)
107
    {
108
        $result['api_status'] = $this->ctrl->hook_api_status ?: $result['api_status'];
109
        $result['api_message'] = $this->ctrl->hook_api_message ?: $result['api_message'];
110
111
        if (cbGetsetting('api_debug_mode') == 'true') {
112
            $result['api_authorization'] = $debugModeMessage;
113
        }
114
115
        $this->ctrl->hookAfter($posts, $result);
116
117
        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

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