Completed
Push — master ( f4cb47...3b959f )
by Arjay
01:18
created

DataTablesEditor   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 624
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 62
lcom 1
cbo 13
dl 0
loc 624
rs 3.416
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A process() 0 20 4
A getUseFriendlyErrorMessage() 0 4 1
A toJson() 0 21 3
B create() 0 56 9
A resolveModel() 0 10 2
A createRules() 0 3 1
A messages() 0 4 1
A createMessages() 0 4 1
A attributes() 0 4 1
A formatErrors() 0 13 1
A restore() 0 6 1
B edit() 0 55 10
A getBuilder() 0 10 2
A editRules() 0 3 1
A editMessages() 0 4 1
A forceDelete() 0 6 1
C remove() 0 60 11
A removeRules() 0 3 1
A removeMessages() 0 4 1
A removeExceptionMessage() 0 4 1
A getModel() 0 4 1
A setModel() 0 6 1
A unguard() 0 6 1
A upload() 0 51 3
A uploadRules() 0 4 1
A getUploadedFilename() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like DataTablesEditor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DataTablesEditor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Exception;
6
use Illuminate\Contracts\Validation\Validator;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\SoftDeletes;
9
use Illuminate\Database\QueryException;
10
use Illuminate\Foundation\Validation\ValidatesRequests;
11
use Illuminate\Http\JsonResponse;
12
use Illuminate\Http\Request;
13
use Illuminate\Http\UploadedFile;
14
use Illuminate\Support\Facades\Storage;
15
use Illuminate\Validation\ValidationException;
16
17
abstract class DataTablesEditor
18
{
19
    use ValidatesRequests;
20
21
    /**
22
     * Action performed by the editor.
23
     *
24
     * @var string|null
25
     */
26
    protected $action = null;
27
28
    /**
29
     * Allowed dataTables editor actions.
30
     *
31
     * @var array
32
     */
33
    protected $actions = [
34
        'create',
35
        'edit',
36
        'remove',
37
        'upload',
38
        'forceDelete',
39
        'restore',
40
    ];
41
42
    /**
43
     * List of custom editor actions.
44
     *
45
     * @var array
46
     */
47
    protected $customActions = [];
48
49
    /**
50
     * @var \Illuminate\Database\Eloquent\Model
51
     */
52
    protected $model = null;
53
54
    /**
55
     * Indicates if all mass assignment is enabled on model.
56
     *
57
     * @var bool
58
     */
59
    protected $unguarded = false;
60
61
    /**
62
     * Upload directory relative to storage path.
63
     *
64
     * @var string
65
     */
66
    protected $uploadDir = 'editor';
67
68
    /**
69
     * Flag to force delete a model.
70
     *
71
     * @var bool
72
     */
73
    protected $forceDeleting = false;
74
75
    /**
76
     * Flag to restore a model from deleted state.
77
     *
78
     * @var bool
79
     */
80
    protected $restoring = false;
81
82
    /**
83
     * Filesystem disk config to use for upload.
84
     *
85
     * @var string
86
     */
87
    protected $disk = 'public';
88
89
    /**
90
     * Current request data that is being processed.
91
     *
92
     * @var array
93
     */
94
    protected $currentData = [];
95
96
    /**
97
     * Process dataTables editor action request.
98
     *
99
     * @param Request $request
100
     * @return JsonResponse|mixed
101
     * @throws DataTablesEditorException
102
     */
103
    public function process(Request $request)
104
    {
105
        $this->action = $request->get('action');
106
107
        if (! in_array($this->action, array_merge($this->actions, $this->customActions))) {
108
            throw new DataTablesEditorException(sprintf('Requested action (%s) not supported!', $this->action));
109
        }
110
111
        try {
112
            return $this->{$this->action}($request);
113
        } catch (Exception $exception) {
114
            $error = config('app.debug')
115
                ? '<strong>Server Error:</strong> ' . $exception->getMessage()
116
                : $this->getUseFriendlyErrorMessage();
117
118
            app('log')->error($exception);
119
120
            return $this->toJson([], [], $error);
121
        }
122
    }
123
124
    /**
125
     * @return string
126
     */
127
    protected function getUseFriendlyErrorMessage()
128
    {
129
        return 'An error occurs while processing your request.';
130
    }
131
132
    /**
133
     * Display success data in dataTables editor format.
134
     *
135
     * @param array $data
136
     * @param array $errors
137
     * @param string $error
138
     * @return JsonResponse
139
     */
140
    protected function toJson(array $data, array $errors = [], $error = '')
141
    {
142
        $code = 200;
143
144
        $response = [
145
            'action' => $this->action,
146
            'data'   => $data,
147
        ];
148
149
        if ($error) {
150
            $code              = 422;
151
            $response['error'] = $error;
152
        }
153
154
        if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
155
            $code                    = 422;
156
            $response['fieldErrors'] = $errors;
157
        }
158
159
        return new JsonResponse($response, $code);
160
    }
161
162
    /**
163
     * Process create action request.
164
     *
165
     * @param Request $request
166
     * @return JsonResponse
167
     * @throws \Exception
168
     */
169
    public function create(Request $request)
170
    {
171
        $model      = $this->resolveModel();
172
        $connection = $model->getConnection();
173
        $affected   = [];
174
        $errors     = [];
175
176
        $connection->beginTransaction();
177
        foreach ($request->get('data') as $data) {
178
            $this->currentData = $data;
179
180
            $instance  = $model->newInstance();
181
            $validator = $this->getValidationFactory()
182
                              ->make(
183
                                  $data,
184
                                  $this->createRules(), $this->messages() + $this->createMessages(),
0 ignored issues
show
Deprecated Code introduced by
The method Yajra\DataTables\DataTab...ditor::createMessages() has been deprecated with message: deprecated since v1.12.0, please use messages() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
185
                                  $this->attributes()
186
                              );
187
            if ($validator->fails()) {
188
                foreach ($this->formatErrors($validator) as $error) {
189
                    $errors[] = $error;
190
                }
191
192
                continue;
193
            }
194
195
            if (method_exists($this, 'creating')) {
196
                $data = $this->creating($instance, $data);
0 ignored issues
show
Bug introduced by
The method creating() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
197
            }
198
199
            if (method_exists($this, 'saving')) {
200
                $data = $this->saving($instance, $data);
0 ignored issues
show
Bug introduced by
The method saving() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
201
            }
202
203
            $instance->fill($data)->save();
204
205
            if (method_exists($this, 'created')) {
206
                $instance = $this->created($instance, $data);
0 ignored issues
show
Bug introduced by
The method created() does not exist on Yajra\DataTables\DataTablesEditor. Did you maybe mean create()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
207
            }
208
209
            if (method_exists($this, 'saved')) {
210
                $instance = $this->saved($instance, $data);
0 ignored issues
show
Bug introduced by
The method saved() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
211
            }
212
213
            $instance->setAttribute('DT_RowId', $instance->getKey());
214
            $affected[] = $instance;
215
        }
216
217
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
218
            $connection->commit();
219
        } else {
220
            $connection->rollBack();
221
        }
222
223
        return $this->toJson($affected, $errors);
224
    }
225
226
    /**
227
     * Resolve model to used.
228
     *
229
     * @return Model|\Illuminate\Database\Eloquent\SoftDeletes
230
     */
231
    protected function resolveModel()
232
    {
233
        if (! $this->model instanceof Model) {
234
            $this->model = new $this->model;
235
        }
236
237
        $this->model->unguard($this->unguarded);
238
239
        return $this->model;
240
    }
241
242
    /**
243
     * Get create action validation rules.
244
     *
245
     * @return array
246
     */
247
    public function createRules() {
248
        return [];
249
    }
250
251
    /**
252
     * Get validation messages.
253
     *
254
     * @return array
255
     */
256
    protected function messages()
257
    {
258
        return [];
259
    }
260
261
    /**
262
     * Get create validation messages.
263
     *
264
     * @return array
265
     * @deprecated deprecated since v1.12.0, please use messages() instead.
266
     */
267
    protected function createMessages()
268
    {
269
        return [];
270
    }
271
272
    /**
273
     * Get custom attributes for validator errors.
274
     *
275
     * @return array
276
     */
277
    public function attributes()
278
    {
279
        return [];
280
    }
281
282
    /**
283
     * @param Validator $validator
284
     * @return array
285
     */
286
    protected function formatErrors(Validator $validator)
287
    {
288
        $errors = [];
289
290
        collect($validator->errors())->each(function ($error, $key) use (&$errors) {
291
            $errors[] = [
292
                'name'   => $key,
293
                'status' => $error[0],
294
            ];
295
        });
296
297
        return $errors;
298
    }
299
300
    /**
301
     * Process restore action request.
302
     *
303
     * @param \Illuminate\Http\Request $request
304
     * @return \Illuminate\Http\JsonResponse
305
     */
306
    public function restore(Request $request)
307
    {
308
        $this->restoring = true;
309
310
        return $this->edit($request);
311
    }
312
313
    /**
314
     * Process edit action request.
315
     *
316
     * @param Request $request
317
     * @return JsonResponse
318
     */
319
    public function edit(Request $request)
320
    {
321
        $connection = $this->getBuilder()->getConnection();
322
        $affected   = [];
323
        $errors     = [];
324
325
        $connection->beginTransaction();
326
        foreach ($request->get('data') as $key => $data) {
327
            $this->currentData = $data;
328
329
            $model     = $this->getBuilder()->findOrFail($key);
330
            $validator = $this->getValidationFactory()
331
                              ->make(
332
                                  $data,
333
                                  $this->editRules($model), $this->messages() + $this->editMessages(),
0 ignored issues
show
Documentation introduced by
$model is of type object|null, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method Yajra\DataTables\DataTablesEditor::editMessages() has been deprecated with message: deprecated since v1.12.0, please use messages() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
334
                                  $this->attributes()
335
                              );
336
            if ($validator->fails()) {
337
                foreach ($this->formatErrors($validator) as $error) {
338
                    $errors[] = $error;
339
                }
340
341
                continue;
342
            }
343
344
            if (method_exists($this, 'updating')) {
345
                $data = $this->updating($model, $data);
0 ignored issues
show
Bug introduced by
The method updating() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
346
            }
347
348
            if (method_exists($this, 'saving')) {
349
                $data = $this->saving($model, $data);
0 ignored issues
show
Bug introduced by
The method saving() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
350
            }
351
352
            $this->restoring ? $model->restore() : $model->fill($data)->save();
353
354
            if (method_exists($this, 'updated')) {
355
                $model = $this->updated($model, $data);
0 ignored issues
show
Bug introduced by
The method updated() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
356
            }
357
358
            if (method_exists($this, 'saved')) {
359
                $model = $this->saved($model, $data);
0 ignored issues
show
Bug introduced by
The method saved() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
360
            }
361
362
            $model->setAttribute('DT_RowId', $model->getKey());
363
            $affected[] = $model;
364
        }
365
366
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
367
            $connection->commit();
368
        } else {
369
            $connection->rollBack();
370
        }
371
372
        return $this->toJson($affected, $errors);
373
    }
374
375
    /**
376
     * Get elqouent builder of the model.
377
     *
378
     * @return \Illuminate\Database\Eloquent\Builder
379
     */
380
    protected function getBuilder()
381
    {
382
        $model = $this->resolveModel();
383
384
        if (in_array(SoftDeletes::class, class_uses($model))) {
385
            return $model->newQuery()->withTrashed();
386
        }
387
388
        return $model->newQuery();
389
    }
390
391
    /**
392
     * Get edit action validation rules.
393
     *
394
     * @param Model $model
395
     * @return array
396
     */
397
    public function editRules(Model $model) {
0 ignored issues
show
Unused Code introduced by
The parameter $model is not used and could be removed.

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

Loading history...
398
        return [];
399
    }
400
401
    /**
402
     * Get edit validation messages.
403
     *
404
     * @return array
405
     * @deprecated deprecated since v1.12.0, please use messages() instead.
406
     */
407
    protected function editMessages()
408
    {
409
        return [];
410
    }
411
412
    /**
413
     * Process force delete action request.
414
     *
415
     * @param \Illuminate\Http\Request $request
416
     * @return \Illuminate\Http\JsonResponse
417
     * @throws \Exception
418
     */
419
    public function forceDelete(Request $request)
420
    {
421
        $this->forceDeleting = true;
422
423
        return $this->remove($request);
424
    }
425
426
    /**
427
     * Process remove action request.
428
     *
429
     * @param Request $request
430
     * @return JsonResponse
431
     * @throws \Exception
432
     */
433
    public function remove(Request $request)
434
    {
435
        $connection = $this->getBuilder()->getConnection();
436
        $affected   = [];
437
        $errors     = [];
438
439
        $connection->beginTransaction();
440
        foreach ($request->get('data') as $key => $data) {
441
            $this->currentData = $data;
442
443
            $model     = $this->getBuilder()->findOrFail($key);
444
            $validator = $this->getValidationFactory()
445
                              ->make(
446
                                  $data,
447
                                  $this->removeRules($model), $this->messages() + $this->removeMessages(),
0 ignored issues
show
Documentation introduced by
$model is of type object|null, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method Yajra\DataTables\DataTab...ditor::removeMessages() has been deprecated with message: deprecated since v1.12.0, please use messages() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
448
                                  $this->attributes()
449
                              );
450
            if ($validator->fails()) {
451
                foreach ($this->formatErrors($validator) as $error) {
452
                    $errors[] = $error['status'];
453
                }
454
455
                continue;
456
            }
457
458
            try {
459
                $deleted = clone $model;
460
                if (method_exists($this, 'deleting')) {
461
                    $this->deleting($model, $data);
0 ignored issues
show
Bug introduced by
The method deleting() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
462
                }
463
464
                $this->forceDeleting ? $model->forceDelete() : $model->delete();
465
466
                if (method_exists($this, 'deleted')) {
467
                    $this->deleted($deleted, $data);
0 ignored issues
show
Bug introduced by
The method deleted() does not seem to exist on object<Yajra\DataTables\DataTablesEditor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
468
                }
469
            } catch (QueryException $exception) {
470
                $error = config('app.debug')
471
                    ? $exception->getMessage()
472
                    : $this->removeExceptionMessage($exception, $model);
0 ignored issues
show
Documentation introduced by
$model is of type object|null, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
473
474
                $errors[] = $error;
475
            }
476
477
            $affected[] = $deleted;
478
        }
479
480
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
481
            $connection->commit();
482
        } else {
483
            $connection->rollBack();
484
        }
485
486
        $response = ['data' => $affected];
487
        if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
488
            $response['error'] = implode("\n", $errors);
489
        }
490
491
        return $this->toJson($affected, [], $errors ?? '');
0 ignored issues
show
Bug introduced by
It seems like $errors ?? '' can also be of type array; however, Yajra\DataTables\DataTablesEditor::toJson() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
492
    }
493
494
    /**
495
     * Get remove action validation rules.
496
     *
497
     * @param Model $model
498
     * @return array
499
     */
500
    public function removeRules(Model $model) {
0 ignored issues
show
Unused Code introduced by
The parameter $model is not used and could be removed.

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

Loading history...
501
        return [];
502
    }
503
504
    /**
505
     * Get remove validation messages.
506
     *
507
     * @return array
508
     * @deprecated deprecated since v1.12.0, please use messages() instead.
509
     */
510
    protected function removeMessages()
511
    {
512
        return [];
513
    }
514
515
    /**
516
     * Get remove query exception message.
517
     *
518
     * @param QueryException $exception
519
     * @param Model $model
520
     * @return string
521
     */
522
    protected function removeExceptionMessage(QueryException $exception, Model $model)
0 ignored issues
show
Unused Code introduced by
The parameter $exception is not used and could be removed.

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

Loading history...
523
    {
524
        return "Record {$model->getKey()} is protected and cannot be deleted!";
525
    }
526
527
    /**
528
     * Get dataTables model.
529
     *
530
     * @return Model
531
     */
532
    public function getModel()
533
    {
534
        return $this->model;
535
    }
536
537
    /**
538
     * Set the dataTables model on runtime.
539
     *
540
     * @param Model|string $model
541
     * @return DataTablesEditor
542
     */
543
    public function setModel($model)
544
    {
545
        $this->model = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model can also be of type string. However, the property $model is declared as type object<Illuminate\Database\Eloquent\Model>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
546
547
        return $this;
548
    }
549
550
    /**
551
     * Set model unguard state.
552
     *
553
     * @param bool $state
554
     * @return $this
555
     */
556
    public function unguard($state = true)
557
    {
558
        $this->unguarded = $state;
559
560
        return $this;
561
    }
562
563
    /**
564
     * Handle uploading of file.
565
     *
566
     * @param \Illuminate\Http\Request $request
567
     * @return \Illuminate\Http\JsonResponse
568
     */
569
    public function upload(Request $request)
570
    {
571
        $field   = $request->input('uploadField');
572
        $storage = Storage::disk($this->disk);
573
574
        try {
575
            $rules      = $this->uploadRules();
576
            $fieldRules = ['upload' => data_get($rules, $field, [])];
577
578
            $this->validate($request, $fieldRules, $this->messages(), $this->attributes());
579
580
            $uploadedFile = $request->file('upload');
581
            $filename     = $this->getUploadedFilename($field, $uploadedFile);
0 ignored issues
show
Bug introduced by
It seems like $uploadedFile defined by $request->file('upload') on line 580 can also be of type array or null; however, Yajra\DataTables\DataTab...::getUploadedFilename() does only seem to accept object<Illuminate\Http\UploadedFile>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
582
            $id           = $storage->putFileAs($this->uploadDir, $uploadedFile, $filename);
583
584
            if (method_exists($this, 'uploaded')) {
585
                $id = $this->uploaded($id);
0 ignored issues
show
Bug introduced by
The method uploaded() does not exist on Yajra\DataTables\DataTablesEditor. Did you maybe mean upload()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
586
            }
587
588
            return response()->json([
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
589
                'action' => $this->action,
590
                'data'   => [],
591
                'files'  => [
592
                    'files' => [
593
                        $id => [
594
                            'filename'      => $id,
595
                            'original_name' => $uploadedFile->getClientOriginalName(),
596
                            'size'          => $uploadedFile->getSize(),
597
                            'directory'     => $this->uploadDir,
598
                            'disk'          => $this->disk,
599
                            'url'           => $storage->url($id),
600
                        ],
601
                    ],
602
                ],
603
                'upload' => [
604
                    'id' => $id,
605
                ],
606
            ]);
607
        } catch (ValidationException $exception) {
608
            return response()->json([
609
                'action'      => $this->action,
610
                'data'        => [],
611
                'fieldErrors' => [
612
                    [
613
                        'name'   => $field,
614
                        'status' => str_replace('upload', $field, $exception->errors()['upload'][0]),
615
                    ],
616
                ],
617
            ]);
618
        }
619
    }
620
621
    /**
622
     * Upload validation rules.
623
     *
624
     * @return array
625
     */
626
    public function uploadRules()
627
    {
628
        return [];
629
    }
630
631
    /**
632
     * @param string $field
633
     * @param UploadedFile $uploadedFile
634
     * @return string
635
     */
636
    protected function getUploadedFilename($field, UploadedFile $uploadedFile)
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed.

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

Loading history...
637
    {
638
        return date('Ymd_His') . '_' . $uploadedFile->getClientOriginalName();
639
    }
640
}
641