Completed
Push — master ( eb2586...ec2395 )
by Sherif
01:59
created

BaseRepository::list()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 2
nc 2
nop 5
1
<?php namespace App\Modules\Core\BaseClasses;
2
3
use App\Modules\Core\Interfaces\BaseRepositoryInterface;
4
use Illuminate\Support\Arr;
5
use Illuminate\Support\Str;
6
7
abstract class BaseRepository implements BaseRepositoryInterface
8
{
9
    /**
10
     * The model implementation.
11
     *
12
     * @var object
13
     */
14
    public $model;
15
    
16
    /**
17
     * Init new object.
18
     *
19
     * @var mixed model
20
     * @return  void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
21
     */
22
    public function __construct($model)
23
    {
24
        $this->model  = $model;
25
    }
26
27
    /**
28
     * Fetch records with relations based on the given params.
29
     *
30
     * @param   string  $relations
31
     * @param   array   $conditions
32
     * @param   integer $perPage
33
     * @param   string  $sortBy
34
     * @param   boolean $desc
35
     * @return collection
36
     */
37
    public function list($relations = [], $conditions = false, $perPage = 15, $sortBy = 'created_at', $desc = true)
38
    {
39
        unset($conditions['page']);
40
        unset($conditions['perPage']);
41
        unset($conditions['sortBy']);
42
        unset($conditions['sort']);
43
        unset($conditions['page']);
44
45
        if (count($conditions)) {
46
            return $this->paginateBy(['and' => $conditions], $perPage ?? 15, $relations, $sortBy ?? 'created_at', $desc ?? true);
0 ignored issues
show
Bug introduced by
It seems like $relations defined by parameter $relations on line 37 can also be of type string; however, App\Modules\Core\BaseCla...epository::paginateBy() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Documentation introduced by
$desc ?? true is of type boolean, but the function expects a integer.

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...
47
        }
48
49
        return $this->paginate($perPage ?? 15, $relations, $sortBy ?? 'created_at', $desc ?? true);
0 ignored issues
show
Bug introduced by
It seems like $relations defined by parameter $relations on line 37 can also be of type string; however, App\Modules\Core\BaseCla...eRepository::paginate() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Documentation introduced by
$desc ?? true is of type boolean, but the function expects a integer.

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...
50
    }
51
52
    /**
53
     * Fetch all records with relations from the storage.
54
     *
55
     * @param  array   $relations
56
     * @param  string  $sortBy
57
     * @param  boolean $desc
58
     * @param  array   $columns
59
     * @return collection
60
     */
61
    public function all($relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
62
    {
63
        $sort = $desc ? 'desc' : 'asc';
64
        return $this->model->with($relations)->orderBy($sortBy, $sort)->get($columns);
65
    }
66
67
    /**
68
     * Fetch all records with relations from storage in pages
69
     * that matche the given query.
70
     *
71
     * @param  string  $query
72
     * @param  integer $perPage
73
     * @param  array   $relations
74
     * @param  string  $sortBy
75
     * @param  boolean $desc
76
     * @param  array   $columns
77
     * @return collection
78
     */
79
    public function search($query, $perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
80
    {
81
        $model            = $this->model->with($relations);
82
        $conditionColumns = $this->model->searchable;
83
        $sort             = $desc ? 'desc' : 'asc';
84
85
        /**
86
         * Construct the select conditions for the model.
87
         */
88
        $model->where(function ($q) use ($query, $conditionColumns, $relations) {
89
90 View Code Duplication
            if (count($conditionColumns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
91
                $column = 'LOWER('.array_shift($conditionColumns).')';
92
                if (Str::contains($column, '->')) {
93
                    $column = $this->wrapJsonSelector($column);
94
                }
95
96
                /**
97
                 * Use the first element in the model columns to construct the first condition.
98
                 */
99
                $q->where(\DB::raw($column), 'LIKE', '%'.strtolower($query).'%');
100
            }
101
102
            /**
103
             * Loop through the rest of the columns to construct or where conditions.
104
             */
105 View Code Duplication
            foreach ($conditionColumns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
                $column = 'LOWER('.$column.')';
107
                if (Str::contains($column, '->')) {
108
                    $column = $this->wrapJsonSelector($column);
109
                }
110
111
                $q->orWhere(\DB::raw($column), 'LIKE', '%'.strtolower($query).'%');
112
            }
113
114
            /**
115
             * Loop through the model relations.
116
             */
117
            foreach ($relations as $relation) {
118
                /**
119
                 * Remove the sub relation if exists.
120
                 */
121
                $relation = explode('.', $relation)[0];
122
123
                /**
124
                 * Try to fetch the relation repository from the core.
125
                 */
126
                if (\Core::$relation()) {
127
                    /**
128
                     * Construct the relation condition.
129
                     */
130
                    $q->orWhereHas($relation, function ($subModel) use ($query, $relation) {
131
132
                        $subModel->where(function ($q) use ($query, $relation) {
133
134
                            /**
135
                             * Get columns of the relation.
136
                             */
137
                            $subConditionColumns = \Core::$relation()->model->searchable;
138
139 View Code Duplication
                            if (count($subConditionColumns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
                                $column = 'LOWER('.array_shift($subConditionColumns).')';
141
                                if (Str::contains($column, '->')) {
142
                                    $column = $this->wrapJsonSelector($column);
143
                                }
144
145
                                /**
146
                                 * Use the first element in the relation model columns to construct the first condition.
147
                                 */
148
                                $q->where(\DB::raw($column), 'LIKE', '%'.strtolower($query).'%');
149
                            }
150
151
                            /**
152
                             * Loop through the rest of the columns to construct or where conditions.
153
                             */
154 View Code Duplication
                            foreach ($subConditionColumns as $subConditionColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
                                $column = 'LOWER('.$subConditionColumn.')';
156
                                if (Str::contains($column, '->')) {
157
                                    $column = $this->wrapJsonSelector($column);
158
                                }
159
                                
160
                                $q->orWhere(\DB::raw($column), 'LIKE', '%'.strtolower($query).'%');
161
                            }
162
                        });
163
                    });
164
                }
165
            }
166
        });
167
        
168
        return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
169
    }
170
    
171
    /**
172
     * Fetch all records with relations from storage in pages.
173
     *
174
     * @param  integer $perPage
175
     * @param  array   $relations
176
     * @param  string  $sortBy
177
     * @param  boolean $desc
178
     * @param  array   $columns
179
     * @return collection
180
     */
181
    public function paginate($perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
182
    {
183
        $sort = $desc ? 'desc' : 'asc';
184
        return $this->model->with($relations)->orderBy($sortBy, $sort)->paginate($perPage, $columns);
185
    }
186
187
    /**
188
     * Fetch all records with relations based on
189
     * the given condition from storage in pages.
190
     *
191
     * @param  array   $conditions array of conditions
192
     * @param  integer $perPage
193
     * @param  array   $relations
194
     * @param  string  $sortBy
195
     * @param  boolean $desc
196
     * @param  array   $columns
197
     * @return collection
198
     */
199
    public function paginateBy($conditions, $perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
200
    {
201
        $conditions = $this->constructConditions($conditions, $this->model);
202
        $sort       = $desc ? 'desc' : 'asc';
203
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->paginate($perPage, $columns);
204
    }
205
    
206
    /**
207
     * Save the given model to the storage.
208
     *
209
     * @param  array $data
210
     * @return mixed
211
     */
212
    public function save(array $data)
213
    {
214
        \Session::put('locale', 'all');
215
        $model      = false;
216
        $modelClass = $this->model;
217
        $relations  = [];
218
219
        \DB::transaction(function () use (&$model, &$relations, $data, $modelClass) {
220
            /**
221
             * If the id is present in the data then select the model for updating,
222
             * else create new model.
223
             * @var array
224
             */
225
            $model = Arr::has($data, 'id') ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
226
            if (! $model) {
227
                \ErrorHandler::notFound(class_basename($modelClass).' with id : '.$data['id']);
228
            }
229
230
            /**
231
             * Construct the model object with the given data,
232
             * and if there is a relation add it to relations array,
233
             * then save the model.
234
             */
235
            foreach ($data as $key => $value) {
236
                /**
237
                 * If the attribute is a relation.
238
                 */
239
                $relation = \Str::camel($key);
240
                if (method_exists($model, $relation) && \Core::$relation()) {
241
                    /**
242
                     * Check if the relation is a collection.
243
                     */
244
                    if (class_basename($model->$relation) == 'Collection') {
245
                        /**
246
                         * If the relation has no value then marke the relation data
247
                         * related to the model to be deleted.
248
                         */
249
                        if (! $value || ! count($value)) {
250
                            $relations[$relation] = 'delete';
251
                        }
252
                    }
253
                    if (is_array($value)) {
254
                        /**
255
                         * Loop through the relation data.
256
                         */
257
                        foreach ($value as $attr => $val) {
258
                            /**
259
                             * Get the relation model.
260
                             */
261
                            $relationBaseModel = \Core::$relation()->model;
262
263
                            /**
264
                             * Check if the relation is a collection.
265
                             */
266
                            if (class_basename($model->$relation) == 'Collection') {
267
                                /**
268
                                 * If the id is present in the data then select the relation model for updating,
269
                                 * else create new model.
270
                                 */
271
                                $relationModel = Arr::has($val, 'id') ? $relationBaseModel->lockForUpdate()->find($val['id']) : new $relationBaseModel;
272
273
                                /**
274
                                 * If model doesn't exists.
275
                                 */
276
                                if (! $relationModel) {
277
                                    \ErrorHandler::notFound(class_basename($relationBaseModel).' with id : '.$val['id']);
278
                                }
279
280
                                /**
281
                                 * Loop through the relation attributes.
282
                                 */
283
                                foreach ($val as $attr => $val) {
284
                                    /**
285
                                     * Prevent the sub relations or attributes not in the fillable.
286
                                     */
287
                                    if (gettype($val) !== 'object' && gettype($val) !== 'array' && array_search($attr, $relationModel->getFillable(), true) !== false) {
288
                                        $relationModel->$attr = $val;
289
                                    }
290
                                }
291
292
                                $relations[$relation][] = $relationModel;
293
                            } else {
294
                                /**
295
                                 * Prevent the sub relations.
296
                                 */
297
                                if (gettype($val) !== 'object' && gettype($val) !== 'array') {
298
                                    /**
299
                                     * If the id is present in the data then select the relation model for updating,
300
                                     * else create new model.
301
                                     */
302
                                    $relationModel = Arr::has($value, 'id') ? $relationBaseModel->lockForUpdate()->find($value['id']) : new $relationBaseModel;
303
304
                                    /**
305
                                     * If model doesn't exists.
306
                                     */
307
                                    if (! $relationModel) {
308
                                        \ErrorHandler::notFound(class_basename($relationBaseModel).' with id : '.$value['id']);
309
                                    }
310
311
                                    foreach ($value as $relationAttribute => $relationValue) {
312
                                        /**
313
                                         * Prevent attributes not in the fillable.
314
                                         */
315
                                        if (array_search($relationAttribute, $relationModel->getFillable(), true) !== false) {
316
                                            $relationModel->$relationAttribute = $relationValue;
317
                                        }
318
                                    }
319
320
                                    $relations[$relation] = $relationModel;
321
                                }
322
                            }
323
                        }
324
                    }
325
                } elseif (array_search($key, $model->getFillable(), true) !== false) {
326
                    /**
327
                     * If the attribute isn't a relation and prevent attributes not in the fillable.
328
                     */
329
                    $model->$key = $value;
330
                }
331
            }
332
333
            /**
334
             * Loop through the relations array.
335
             */
336
            foreach ($relations as $key => $value) {
337
                /**
338
                 * If the relation is marked for delete then delete it.
339
                 */
340
                if ($value == 'delete' && $model->$key()->count()) {
341
                    $model->$key()->delete();
342
                } elseif (gettype($value) == 'array') {
343
                    /**
344
                     * Save the model.
345
                     */
346
                    $model->save();
347
                    $ids = [];
348
349
                    /**
350
                     * Loop through the relations.
351
                     */
352
                    foreach ($value as $val) {
353
                        switch (class_basename($model->$key())) {
354
                            /**
355
                             * If the relation is one to many then update it's foreign key with
356
                             * the model id and save it then add its id to ids array to delete all
357
                             * relations who's id isn't in the ids array.
358
                             */
359
                            case 'HasMany':
360
                                $foreignKeyName       = $model->$key()->getForeignKeyName();
361
                                $val->$foreignKeyName = $model->id;
362
                                $val->save();
363
                                $ids[] = $val->id;
364
                                break;
365
366
                            /**
367
                             * If the relation is many to many then add it's id to the ids array to
368
                             * attache these ids to the model.
369
                             */
370
                            case 'BelongsToMany':
371
                                $val->save();
372
                                $ids[] = $val->id;
373
                                break;
374
                        }
375
                    }
376
                    switch (class_basename($model->$key())) {
377
                        /**
378
                         * If the relation is one to many then delete all
379
                         * relations who's id isn't in the ids array.
380
                         */
381
                        case 'HasMany':
382
                            $model->$key()->whereNotIn('id', $ids)->delete();
383
                            break;
384
385
                        /**
386
                         * If the relation is many to many then
387
                         * detach the previous data and attach
388
                         * the ids array to the model.
389
                         */
390
                        case 'BelongsToMany':
391
                            $model->$key()->detach();
392
                            $model->$key()->attach($ids);
393
                            break;
394
                    }
395
                } else {
396
                    switch (class_basename($model->$key())) {
397
                        /**
398
                         * If the relation is one to one.
399
                         */
400
                        case 'HasOne':
401
                            /**
402
                             * Save the model.
403
                             */
404
                            $model->save();
405
                            $foreignKeyName         = $model->$key()->getForeignKeyName();
406
                            $value->$foreignKeyName = $model->id;
407
                            $value->save();
408
                            break;
409
                        case 'BelongsTo':
410
                            /**
411
                             * Save the model.
412
                             */
413
                            $value->save();
414
                            $model->$key()->associate($value);
415
                            break;
416
                    }
417
                }
418
            }
419
420
            /**
421
             * Save the model.
422
             */
423
            $model->save();
424
        });
425
            
426
        return $model;
427
    }
428
429
    /**
430
     * Delete record from the storage based on the given
431
     * condition.
432
     *
433
     * @param  var $value condition value
434
     * @param  string $attribute condition column name
435
     * @return void
436
     */
437
    public function delete($value, $attribute = 'id')
438
    {
439
        if ($attribute == 'id') {
440 View Code Duplication
            \DB::transaction(function () use ($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
                $model = $this->model->lockForUpdate()->find($value);
442
                if (! $model) {
443
                    \ErrorHandler::notFound(class_basename($this->model).' with id : '.$value);
444
                }
445
                
446
                $model->delete();
447
            });
448
        } else {
449
            \DB::transaction(function () use ($value, $attribute) {
450
                $this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function ($model) {
451
                    $model->delete();
452
                });
453
            });
454
        }
455
    }
456
    
457
    /**
458
     * Fetch records from the storage based on the given
459
     * id.
460
     *
461
     * @param  integer $id
462
     * @param  string[]   $relations
463
     * @param  array   $columns
464
     * @return object
465
     */
466
    public function find($id, $relations = [], $columns = ['*'])
467
    {
468
        return $this->model->with($relations)->find($id, $columns);
469
    }
470
    
471
    /**
472
     * Fetch records from the storage based on the given
473
     * condition.
474
     *
475
     * @param  array   $conditions array of conditions
476
     * @param  array   $relations
477
     * @param  string  $sortBy
478
     * @param  boolean $desc
479
     * @param  array   $columns
480
     * @return collection
481
     */
482
    public function findBy($conditions, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
483
    {
484
        $conditions = $this->constructConditions($conditions, $this->model);
485
        $sort       = $desc ? 'desc' : 'asc';
486
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->get($columns);
487
    }
488
489
    /**
490
     * Fetch the first record from the storage based on the given
491
     * condition.
492
     *
493
     * @param  array   $conditions array of conditions
494
     * @param  array   $relations
495
     * @param  array   $columns
496
     * @return object
497
     */
498
    public function first($conditions, $relations = [], $columns = ['*'])
499
    {
500
        $conditions = $this->constructConditions($conditions, $this->model);
501
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->first($columns);
502
    }
503
504
    /**
505
     * Return the deleted models in pages based on the given conditions.
506
     *
507
     * @param  array   $conditions array of conditions
508
     * @param  integer $perPage
509
     * @param  string  $sortBy
510
     * @param  boolean $desc
511
     * @param  array   $columns
512
     * @return collection
513
     */
514
    public function deleted($conditions, $perPage = 15, $sortBy = 'created_at', $desc = 1, $columns = ['*'])
515
    {
516
        unset($conditions['page']);
517
        unset($conditions['perPage']);
518
        unset($conditions['sortBy']);
519
        unset($conditions['sort']);
520
        $conditions = $this->constructConditions($conditions, $this->model);
521
        $sort       = $desc ? 'desc' : 'asc';
522
        $model      = $this->model->onlyTrashed();
523
524
        if (count($conditions['conditionValues'])) {
525
            $model->whereRaw($conditions['conditionString'], $conditions['conditionValues']);
526
        }
527
528
        return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
529
    }
530
531
    /**
532
     * Restore the deleted model.
533
     *
534
     * @param  integer $id
535
     * @return void
536
     */
537 View Code Duplication
    public function restore($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
538
    {
539
        $model = $this->model->onlyTrashed()->find($id);
540
541
        if (! $model) {
542
            \ErrorHandler::notFound(class_basename($this->model).' with id : '.$id);
543
        }
544
545
        $model->restore();
546
    }
547
548
    /**
549
     * Build the conditions recursively for the retrieving methods.
550
     * @param  array $conditions
551
     * @return array
552
     */
553
    protected function constructConditions($conditions, $model)
554
    {
555
        $conditionString = '';
556
        $conditionValues = [];
557
        foreach ($conditions as $key => $value) {
558
            if (Str::contains($key, '->')) {
559
                $key = $this->wrapJsonSelector($key);
560
            }
561
562
            if ($key == 'and') {
563
                $conditions       = $this->constructConditions($value, $model);
564
                $conditionString .= str_replace('{op}', 'and', $conditions['conditionString']).' {op} ';
565
                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
566
            } elseif ($key == 'or') {
567
                $conditions       = $this->constructConditions($value, $model);
568
                $conditionString .= str_replace('{op}', 'or', $conditions['conditionString']).' {op} ';
569
                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
570
            } else {
571
                if (is_array($value)) {
572
                    $operator = $value['op'];
573
                    if (strtolower($operator) == 'between') {
574
                        $value1 = $value['val1'];
575
                        $value2 = $value['val2'];
576
                    } else {
577
                        $value = Arr::get($value, 'val', '');
578
                    }
579
                } else {
580
                    $operator = '=';
581
                }
582
                
583
                if (strtolower($operator) == 'between') {
584
                    $conditionString  .= $key.' >= ? and ';
585
                    $conditionValues[] = $value1;
0 ignored issues
show
Bug introduced by
The variable $value1 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
586
587
                    $conditionString  .= $key.' <= ? {op} ';
588
                    $conditionValues[] = $value2;
0 ignored issues
show
Bug introduced by
The variable $value2 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
589
                } elseif (strtolower($operator) == 'in') {
590
                    $conditionValues  = array_merge($conditionValues, $value);
591
                    $inBindingsString = rtrim(str_repeat('?,', count($value)), ',');
592
                    $conditionString .= $key.' in ('.rtrim($inBindingsString, ',').') {op} ';
593
                } elseif (strtolower($operator) == 'null') {
594
                    $conditionString .= $key.' is null {op} ';
595
                } elseif (strtolower($operator) == 'not null') {
596
                    $conditionString .= $key.' is not null {op} ';
597
                } elseif (strtolower($operator) == 'has') {
598
                    $sql              = $model->withTrashed()->has($key)->toSql();
599
                    $conditions       = $this->constructConditions($value, $model->$key()->getRelated());
600
                    $conditionString .= rtrim(substr($sql, strpos($sql, 'exists')), ')').' and '.$conditions['conditionString'].') {op} ';
601
                    $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
602
                } else {
603
                    $conditionString  .= $key.' '.$operator.' ? {op} ';
604
                    $conditionValues[] = $value;
605
                }
606
            }
607
        }
608
        $conditionString = '('.rtrim($conditionString, '{op} ').')';
609
        return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
610
    }
611
612
    /**
613
     * Wrap the given JSON selector.
614
     *
615
     * @param  string  $value
616
     * @return string
617
     */
618
    protected function wrapJsonSelector($value)
619
    {
620
        $removeLast = strpos($value, ')');
621
        $value      = $removeLast === false ? $value : substr($value, 0, $removeLast);
622
        $path       = explode('->', $value);
623
        $field      = array_shift($path);
624
        $result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
625
            return '"'.$part.'"';
626
        })->implode('.'));
627
        
628
        return $removeLast === false ? $result : $result.')';
629
    }
630
}
631