Passed
Push — master ( 396998...e80d0c )
by Iman
05:38
created

ExecuteApi::execute()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 60
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 34
nc 10
nop 0
dl 0
loc 60
rs 7.0677
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
        $row_api = DB::table('cms_apicustom')->where('permalink', $this->ctrl->permalink)->first();
29
30
        $actionType = $row_api->aksi;
31
        $table = $row_api->tabel;
32
33
        /* Do some custome pre-checking for posted data, if failed discard API execution */
34
        $this->doCustomePrecheck();
35
36
        /* Method Type validation */
37
        $methodType = $row_api->method_type;
38
        $this->validateMethodType($methodType);
39
40
        /* Check the row is exists or not */
41
        $this->checkApiDefined($row_api);
42
43
        @$parameters = unserialize($row_api->parameters);
44
        list($type_except, $input_validator) = $this->validateParams($parameters, $table);
45
46
        @$responses = unserialize($row_api->responses);
47
        $responses_fields = $this->prepareResponses($responses);
48
49
        $posts = request()->all();
50
        $this->ctrl->hookBefore($posts);
51
52
53
        $orderby = ($posts['orderby']) ?: $table.'.id,desc';
54
55
        unset($posts['limit'], $posts['offset'], $posts['orderby']);
56
57
        if (in_array($actionType, ['list', 'detail', 'delete'])) {
58
            $data = $this->fetchDataFromDB($table, $responses, $responses_fields, $parameters, $posts);
59
60
            $this->filterRows($data, $parameters, $posts, $table, $type_except);
61
62
            //IF SQL WHERE IS NOT NULL
63
            if ($row_api->sql_where) {
64
                $data->whereraw($row_api->sql_where);
65
            }
66
67
            $this->ctrl->hookQuery($data);
68
69
            $result = [];
70
            if ($actionType == 'list') {
71
                $result = $this->handleListAction($table, $orderby, $data, $responses_fields);
72
            } elseif ($actionType == 'detail') {
73
                $result = $this->handleDetailsAction($data, $parameters, $posts, $responses_fields);
74
            } elseif ($actionType == 'delete') {
75
                $result = $this->handleDeleteAction($table, $data);
76
            }
77
        } elseif (in_array($actionType, ['save_add', 'save_edit'])) {
78
            $rowAssign = array_filter($input_validator, function ($column) use ($table) {
79
                return Schema::hasColumn($table, $column);
80
            }, ARRAY_FILTER_USE_KEY);
81
82
            $this->handleAddEdit($parameters, $posts, $rowAssign);
83
        }
84
85
        $this->show($result, $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...
86
    }
87
88
    /**
89
     * @param $result
90
     * @param $posts
91
     * @return mixed
92
     */
93
    private function show($result, $posts)
94
    {
95
        $result['api_status'] = $this->ctrl->hook_api_status ?: $result['api_status'];
96
        $result['api_message'] = $this->ctrl->hook_api_message ?: $result['api_message'];
97
98
        if (cbGetsetting('api_debug_mode') == 'true') {
99
            $result['api_authorization'] = 'You are in debug mode !';
100
        }
101
102
        $this->ctrl->hookAfter($posts, $result);
103
104
        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

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

598
        $data->skip(/** @scrutinizer ignore-type */ request('offset', 0));
Loading history...
599
        $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

599
        $data->take(/** @scrutinizer ignore-type */ request('limit', 20));
Loading history...
600
        $data = $this->responses($table, $data, $responses, $responses_fields); //End Responses
601
602
        $this->params($parameters, $posts, $data, $table);
603
604
        if (\Schema::hasColumn($table, 'deleted_at')) {
605
            $data->where($table.'.deleted_at', null);
606
        }
607
608
        return $data;
609
    }
610
611
    /**
612
     * @param $status
613
     * @param $msg
614
     * @return array
615
     */
616
    private function makeResult($status, $msg)
617
    {
618
        $result = [];
619
        $result['api_status'] = $status;
620
        $result['api_message'] = $msg;
621
        if (cbGetsetting('api_debug_mode') == 'true') {
622
            $result['api_authorization'] = 'You are in debug mode !';
623
        }
624
625
        return $result;
626
    }
627
}