Passed
Push — master ( 70d4c6...21d85a )
by Iman
05:00
created

ExecuteApi::handleFile()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

428
        $data->take(/** @scrutinizer ignore-type */ request('limit', 20));
Loading history...
429
        $data = $this->responses($table, $data, $responses, $responsesFields); //End Responses
430
431
        $this->params($parameters, $posts, $data, $table);
432
433
        if (\Schema::hasColumn($table, 'deleted_at')) {
434
            $data->where($table.'.deleted_at', null);
435
        }
436
437
        return $data;
438
    }
439
440
    /**
441
     * @param $table
442
     * @param $data
443
     * @return mixed
444
     */
445
    private function sortRows($table, $data)
446
    {
447
        $orderBy = request('orderby', $table.'.id,desc');
448
449
        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

449
        list($orderByCol, $orderByVal) = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
450
451
        $rows = $data->orderby($orderByCol, $orderByVal)->get();
452
453
        return $rows;
454
    }
455
}