RestApiTrait::update()   B
last analyzed

Complexity

Conditions 8
Paths 15

Size

Total Lines 47
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
cc 8
eloc 27
c 5
b 1
f 0
nc 15
nop 2
dl 0
loc 47
rs 8.4444
1
<?php
2
3
namespace Crystoline\LaraRestApi;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Http\JsonResponse;
0 ignored issues
show
Bug introduced by
The type Illuminate\Http\JsonResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Illuminate\Http\Request;
0 ignored issues
show
Bug introduced by
The type Illuminate\Http\Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Illuminate\Support\Facades\DB;
10
use Illuminate\Support\Facades\Storage;
11
use Illuminate\Support\Facades\Validator;
12
13
/**
14
 * Rest Api trait for Api Controller. Provide CRUD.
15
 */
16
trait RestApiTrait
17
{
18
    /**
19
     * Status Codes response ok.
20
     * @var int
21
     */
22
    public static $STATUS_CODE_DONE = 200;
23
    /**
24
     * Status Codes response created.
25
     * @var int
26
     */
27
    public static $STATUS_CODE_CREATED = 201;
28
    /**
29
     * Status Codes response deleted.
30
     * @var int
31
     */
32
    public static $STATUS_CODE_REMOVED = 204;
33
    /**
34
     * Status Codes invalid response.
35
     * @var int
36
     */
37
    public static $STATUS_CODE_NOT_VALID = 400;
38
    /**
39
     * Status Codes response not allowed.
40
     * @var int
41
     */
42
    public static $STATUS_CODE_NOT_ALLOWED = 405;
43
    /**
44
     * Status Codes response not created.
45
     * @var int
46
     */
47
    public static $STATUS_CODE_NOT_CREATED = 406;
48
    /**
49
     * Status Codes response not found.
50
     * @var int
51
     */
52
    public static $STATUS_CODE_NOT_FOUND = 404;
53
    /**
54
     * Status Codes response duplicate.
55
     * @var int
56
     */
57
    public static $STATUS_CODE_CONFLICT = 409;
58
    /**
59
     * Status Codes response Unauthorized.
60
     * @var int
61
     */
62
    public static $STATUS_CODE_PERMISSION = 401;
63
    /**
64
     * Status Codes response Access Denied.
65
     * @var int
66
     */
67
    public static $STATUS_CODE_FORBIDDEN = 403;
68
    /**
69
     * Status Codes response Server Error.
70
     * @var int
71
     */
72
    public static $STATUS_CODE_SERVER_ERROR = 500;
73
    /**
74
     * Status Codes response no data.
75
     * @var int
76
     */
77
    public static $STATUS_CODE_NO_RECORD = 407;
78
    protected $statusCodes = [
79
        'done' => 200,
80
        'created' => 201,
81
        'removed' => 204,
82
        'not_valid' => 400,
83
        'not_found' => 404,
84
        'not_record' => 407,
85
        'conflict' => 409,
86
        'permissions' => 401,
87
        'server_error' => 500,
88
    ];
89
90
91
    /**
92
     * @var int Number of pages
93
     */
94
    protected $pages = 50;
95
96
    /**
97
     * List Objects.
98
     * @param Request $request
99
     * @return JsonResponse
100
     */
101
    public function index(Request $request): JsonResponse
102
    {
103
104
        /** @var Model $m */
105
        $m = self::getModel();
106
        $data = $m::query();
107
        $pages = self::getPages();
108
        $searchables = self::searchable();
109
        $orderBy = self::orderBy() ?: [];
110
        self::filter($request, $data);
111
        self::doSearch($request, $data, $searchables);
112
        self::doOrderBy($request, $data, $orderBy);
113
        $data = self::paginate($request, $data, $pages);
114
115
        if ($data instanceof Builder) {
116
            $data = $data->get();
117
        }
118
119
        $this->beforeList($data);
120
        return $this->respond(self::$STATUS_CODE_DONE, $data);
121
122
    }
123
124
    /**
125
     * get The Model name used. with full namespace
126
     * @return string
127
     */
128
    public static function getModel(): string
129
    {
130
        return Model::class;
131
    }
132
133
    /**
134
     * return number pages for pagination
135
     * @return int
136
     */
137
    public static function getPages(): int
138
    {
139
        return 50;
140
    }
141
142
    /**
143
     * Return array of searchable fields
144
     * @return array
145
     */
146
    public static function searchable(): array
147
    {
148
        return [];
149
    }
150
151
    /**
152
     * @return array
153
     */
154
    public static function orderBy(): array
155
    {
156
        return [
157
        ];
158
    }
159
160
    /**
161
     * Filter data using request
162
     * @param Request $request
163
     * @param $query
164
     */
165
    public static function filter(Request $request, $query)
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed. ( Ignorable by Annotation )

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

165
    public static function filter(Request $request, /** @scrutinizer ignore-unused */ $query)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

165
    public static function filter(/** @scrutinizer ignore-unused */ Request $request, $query)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
166
    {
167
    }
168
169
    /**
170
     * Perform wild-card search
171
     * @param Request $request
172
     * @param Builder $builder
173
     * @param $searchables
174
     * return none, Builder passed by reference
175
     */
176
    public static function doSearch(Request $request, Builder $builder, $searchables) /*:Builder*/
177
    {
178
        $builder->where(function (Builder $builder) use ($request, $searchables) {
179
            if ($search = $request->input('search')) {
180
                $keywords = explode(' ', trim($search));
181
                if ($searchables) {
182
                    $i = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $i is dead and can be removed.
Loading history...
183
                    foreach ($searchables as $searchable) {
184
                        foreach ($keywords as $keyword) {
185
                            $builder->orWhere($searchable, 'like', "%{$keyword}%");
186
                        }
187
                    }
188
                }
189
            }
190
            if ($search = $request->input('qsearch')) {
191
                if ($searchables) {
192
                    $i = 0;
193
                    foreach ($searchables as $searchable) {
194
                        $builder->orWhere($searchable, 'like', "%{$search}%");
195
                    }
196
                }
197
            }
198
        });
199
200
        //return $builder;
201
    }
202
203
    /**
204
     * Order Data
205
     * @param Request $request
206
     * @param Builder $builder
207
     * @param array $orderBy
208
     */
209
    public static function doOrderBy(Request $request, Builder $builder, array $orderBy)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

209
    public static function doOrderBy(/** @scrutinizer ignore-unused */ Request $request, Builder $builder, array $orderBy)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
210
    {
211
        if ($orderBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $orderBy of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
212
            foreach ($orderBy as $field => $direction) {
213
                $builder->orderBy($field, $direction);
214
            }
215
        }
216
    }
217
218
    /**
219
     * Paginate Data
220
     * @param Request $request
221
     * @param Builder $data
222
     * @param int $pages
223
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|Builder
224
     */
225
    public static function paginate(Request $request, $data, $pages = 50)
226
    {
227
        $should_paginate = $request->input('paginate', 'yes');
228
229
        if ('yes' == $should_paginate) {
230
            $data = $data->paginate($request->input('pages', $pages));
231
        }
232
233
        return $data;
234
    }
235
236
    /**
237
     * Perform action before data list
238
     * @param $data
239
     */
240
    public function beforeList($data)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

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

240
    public function beforeList(/** @scrutinizer ignore-unused */ $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
241
    {
242
    }
243
244
    /**
245
     * Show records.
246
     * @param $id
247
     * @return JsonResponse
248
     */
249
    public function show(int $id)
250
    {
251
        $m = self::getModel();
252
        $data = $m::find($id);
253
254
        if (is_null($data)) {
255
            return $this->respond(self::$STATUS_CODE_NOT_FOUND, ['message' => 'Record was not found']);
256
        }
257
        $this->beforeShow($data);
258
259
        return $this->respond(self::$STATUS_CODE_DONE, $data);
260
261
    }
262
263
    /**
264
     * Perform action before data show
265
     * @param $data
266
     */
267
    public function beforeShow($data)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

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

267
    public function beforeShow(/** @scrutinizer ignore-unused */ $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
268
    {
269
    }
270
271
    /**
272
     * Store Record.
273
     * @param Request $request
274
     * @return JsonResponse
275
     */
276
    public function store(Request $request): JsonResponse
277
    {
278
        /** @var Model $m */
279
        $m = self::getModel();
280
        $rules = self::getValidationRules();
281
        $message = self::getValidationMessages();
282
283
        $validator = Validator::make($request->all(), $rules, $message);
284
        //$this->validate($request, $rules, $message);
285
286
        if ($validator->fails()) {
287
            return $this->respond(self::$STATUS_CODE_NOT_VALID, $validator->errors());
288
          // return  response()->json($validator->errors(), self::$STATUS_CODE_NOT_VALID);
289
        }
290
291
        DB::beginTransaction();
292
293
        //try{
294
        if (!$this->beforeStore($request)) {
295
            DB::rollback();
296
            return $this->respond(self::$STATUS_CODE_SERVER_ERROR, ['message' => 'could not create record (Duplicate Record)']);
297
           // return response()->json(['message' => 'could not create record (Duplicate Record)'], self::$STATUS_CODE_SERVER_ERROR);
298
        }
299
        self::doUpload($request);
300
        $input = $request->input();
301
302
303
        /*unset($input['school']);
304
        unset($input['staff']);*/
305
306
        //dump($input);
307
        $data = $m::create($input);
308
309
        //catch (\Exception $exception){
310
        //DB::rollback();
311
        //todo remove Exception message
312
        //return response()->json( ['message' => 'An error occurred while creating record: '.$exception->getMessage().', Line:'.$exception->getFile().'/'.$exception->getLine()], self::$STATUS_CODE_CONFLICT);
313
        //}
314
        if (!$this->afterStore($request, $data)) {
315
            DB::rollback();
316
317
            return $this->respond(self::$STATUS_CODE_SERVER_ERROR, ['message' => 'could not successfully create record']);
318
           // return response()->json(['message' => 'could not successfully create record'], self::$STATUS_CODE_SERVER_ERROR);
319
        }
320
321
        DB::commit();
322
323
324
        $this->beforeShow($data);
325
        return $this->respond(self::$STATUS_CODE_CREATED, $data);
0 ignored issues
show
Bug introduced by
$data of type Illuminate\Database\Eloquent\Model is incompatible with the type array expected by parameter $data of Crystoline\LaraRestApi\RestApiTrait::respond(). ( Ignorable by Annotation )

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

325
        return $this->respond(self::$STATUS_CODE_CREATED, /** @scrutinizer ignore-type */ $data);
Loading history...
326
        //return response()->json($data, self::$STATUS_CODE_CREATED);
327
    }
328
329
    /**
330
     * @return array
331
     */
332
    public static function getValidationRules(): array
333
    {
334
        return [];
335
    }
336
337
    /**
338
     * @return array
339
     */
340
    public static function getValidationMessages(): array
341
    {
342
        return [];
343
    }
344
345
    /**
346
     * Perform action before data store
347
     * @param Request $request
348
     * @return bool
349
     */
350
    public function beforeStore(Request $request): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

350
    public function beforeStore(/** @scrutinizer ignore-unused */ Request $request): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
351
    {
352
        return true;
353
    }
354
355
    /**
356
     * Perform file upload for request
357
     * @param Request $request
358
     * @param Model $object
359
     */
360
    public static function doUpload(Request $request, $object = null)
361
    {
362
        //dd('kdkd');
363
        $data = $request->all();
364
        foreach ($data as $key => $val) {
365
366
            if ($request->hasFile($key) && $request->file($key)->isValid()) {
367
368
                $original = $object->$key ?? null;
369
370
                $interfaces = class_implements(self::class);
371
                $base = isset($interfaces[IFileUpload::class]) ? self::fileBasePath($request) : '';
372
                if ($base) {
373
                    $base = trim($base, '/,\\') . '/';
374
                }
375
                $path = $request->$key->store('public/' . $base . $key);
376
                $path = str_replace('public/', 'storage/', $path);
377
378
                $path_url = asset($path);
0 ignored issues
show
Bug introduced by
The function asset was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

378
                $path_url = /** @scrutinizer ignore-call */ asset($path);
Loading history...
379
380
                $request->files->remove($key);
381
                $request->merge([$key => $path_url]);
382
383
                if (!is_null($original)) {
384
                    Storage::delete(str_replace('storage/', 'public/', $original));
385
                }
386
            }
387
        }
388
389
    }
390
391
    /**
392
     * Perform action after data store
393
     * @param Request $request
394
     * @param $data
395
     * @return bool
396
     */
397
    public function afterStore(Request $request, $data): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

397
    public function afterStore(/** @scrutinizer ignore-unused */ Request $request, $data): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
398
    {
399
        $this->beforeShow($data);
400
        return true;
401
    }
402
403
    /**
404
     * Update Record.
405
     *
406
     * @param Request $request
407
     * @param $id
408
     *
409
     * @return JsonResponse
410
     */
411
    public function update(Request $request, int $id): JsonResponse
412
    {
413
        /** @var Model $m */
414
        $m = self::getModel();
415
        $model = $m::find($id);
416
417
        if ($model === null) {
418
            return $this->respond(self::$STATUS_CODE_NOT_FOUND, ['message' => 'Record was not found']);
419
        }
420
421
        $rules = self::getValidationRulesForUpdate($model);
422
        $message = self::getValidationMessages();
423
        //$this->validate($request, $rules, $message);
424
        $validator = Validator::make($request->all(), $rules, $message);
425
         if ($validator->fails()) {
426
             return $this->respond(self::$STATUS_CODE_NOT_VALID, $validator->errors());
427
             //return  response()->json($validator->errors(), self::$STATUS_CODE_NOT_VALID);
428
         }
429
430
        DB::beginTransaction();
431
        if (!$this->beforeUpdate($request)) {
432
            DB::rollback();
433
            return $this->respond(self::$STATUS_CODE_SERVER_ERROR, ['message' => 'could not update record']);
434
            //return response()->json(['message' => 'could not update record'], self::$STATUS_CODE_SERVER_ERROR);
435
        }
436
        self::doUpload($request, $model);
437
        $fieldsToUpdate = (method_exists($model, 'fieldsToUpdate')
438
            and !empty(self::fieldsToUpdate())) ?
439
            $request->only(self::fieldsToUpdate()) : $request->input();
440
441
        try {
442
            $model->update($fieldsToUpdate);
443
        } catch (\Exception $exception) {
444
            DB::rollback();
445
            //todo remove Exception message
446
           // return response()->json(['message' => 'An error occurred while updating record: '], 500);
447
            return $this->respond(self::$STATUS_CODE_SERVER_ERROR, ['message' => 'An error occurred while updating record: ']);
448
        }
449
        if (!$this->afterUpdate($request, $model)) {
450
            DB::rollback();
451
            return $this->respond(self::$STATUS_CODE_SERVER_ERROR, ['message' => 'could not successfully update record']);
452
            //return response()->json(['message' => 'could not successfully update record'], self::$STATUS_CODE_SERVER_ERROR);
453
        }
454
455
        DB::commit();
456
457
        return $this->respond(self::$STATUS_CODE_DONE, $model);
0 ignored issues
show
Bug introduced by
$model of type Illuminate\Database\Eloquent\Model is incompatible with the type array expected by parameter $data of Crystoline\LaraRestApi\RestApiTrait::respond(). ( Ignorable by Annotation )

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

457
        return $this->respond(self::$STATUS_CODE_DONE, /** @scrutinizer ignore-type */ $model);
Loading history...
458
        //return response()->json($model, self::$STATUS_CODE_DONE);
459
    }
460
461
    /**
462
     * @param Model $model
463
     * @return array
464
     */
465
    public static function getValidationRulesForUpdate(Model $model): array
466
    {
467
        $id = $model->id;
468
        $rules = self::getValidationRules();
469
        $fields = self::getUniqueFields();
470
        foreach ($fields as $field) {
471
            if (isset($rules[$field])) {
472
                $rules[$field] .= ',' . $id;
473
            }
474
        }
475
        return $rules;
476
    }
477
478
    /**
479
     * Return array of unique field. Used for validation
480
     * @return array
481
     */
482
    public static function getUniqueFields(): array
483
    {
484
        return [];
485
    }
486
487
    /**
488
     * Run before update action
489
     * @param Request $request
490
     * @return bool
491
     */
492
    public function beforeUpdate(Request $request): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

492
    public function beforeUpdate(/** @scrutinizer ignore-unused */ Request $request): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
493
    {
494
        return true;
495
    }
496
497
    /**
498
     * get fields to be update
499
     * @return array
500
     */
501
    public static function fieldsToUpdate(): array
502
    {
503
        return [];
504
    }
505
506
    /**
507
     * Run after update action
508
     * @param Request $request
509
     * @param $data
510
     * @return bool
511
     */
512
    public function afterUpdate(Request $request, $data): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

512
    public function afterUpdate(/** @scrutinizer ignore-unused */ Request $request, $data): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
513
    {
514
        $this->beforeShow($data);
515
        return true;
516
    }
517
518
    /**
519
     * Delete Record.
520
     *
521
     * @param $id
522
     *
523
     * @return JsonResponse
524
     */
525
    public function destroy(int $id)
526
    {
527
        $m = self::getModel();
528
        if ($m::find($id) === null) {
529
            //return response()->json(['record was not found'], self::$STATUS_CODE_NOT_FOUND);
530
            return $this->respond(self::$STATUS_CODE_NOT_FOUND, ['message' => 'record was not found']);
531
        }
532
        try {
533
            $m::destroy($id);
534
        } catch (\Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
535
536
        }
537
538
        return $this->respond(self::$STATUS_CODE_DONE, ['message' => 'record was deleted']);
539
        //return response()->json(['message' => 'record was deleted'], self::$STATUS_CODE_DONE);
540
    }
541
542
    /**
543
     * Perform action before data deletion
544
     * @param Request $request
545
     * @return bool
546
     */
547
    public function beforeDelete(Request $request): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

547
    public function beforeDelete(/** @scrutinizer ignore-unused */ Request $request): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
548
    {
549
        return true;
550
    }
551
552
    /**
553
     * @param $status
554
     * @param array $data
555
     *
556
     * @return JsonResponse
557
     */
558
    protected function respond($status, $data = []): JsonResponse
559
    {
560
        $status = array_key_exists($status, $this->statusCodes)? $this->statusCodes[$status]: $status;
561
        return response()->json($data, $status);
0 ignored issues
show
Bug introduced by
The function response was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

561
        return /** @scrutinizer ignore-call */ response()->json($data, $status);
Loading history...
562
    }
563
564
565
}
566