Completed
Push — master ( d2b124...4b6f70 )
by Sherif
02:08
created

BaseRepository::constructConditions()   C

Complexity

Conditions 12
Paths 41

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 60
rs 6.446
c 0
b 0
f 0
cc 12
nc 41
nop 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Modules\Core\BaseClasses;
4
5
use App\Modules\Core\Interfaces\BaseRepositoryInterface;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
9
abstract class BaseRepository implements BaseRepositoryInterface
10
{
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 all records with relations from the storage.
29
     *
30
     * @param  array   $relations
31
     * @param  string  $sortBy
32
     * @param  boolean $desc
33
     * @param  array   $columns
34
     * @return collection
35
     */
36
    public function all($relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
37
    {
38
        $sort = $desc ? 'desc' : 'asc';
39
        return $this->model->with($relations)->orderBy($sortBy, $sort)->get($columns);
40
    }
41
    
42
    /**
43
     * Fetch all records with relations from storage in pages.
44
     *
45
     * @param  integer $perPage
46
     * @param  array   $relations
47
     * @param  string  $sortBy
48
     * @param  boolean $desc
49
     * @param  array   $columns
50
     * @return collection
51
     */
52
    public function paginate($perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
53
    {
54
        $sort = $desc ? 'desc' : 'asc';
55
        return $this->model->with($relations)->orderBy($sortBy, $sort)->paginate($perPage, $columns);
56
    }
57
58
    /**
59
     * Fetch all records with relations based on
60
     * the given condition from storage in pages.
61
     *
62
     * @param  array   $conditions array of conditions
63
     * @param  integer $perPage
64
     * @param  array   $relations
65
     * @param  string  $sortBy
66
     * @param  boolean $desc
67
     * @param  array   $columns
68
     * @return collection
69
     */
70
    public function paginateBy($conditions, $perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
71
    {
72
        $conditions = $this->constructConditions($conditions, $this->model);
73
        $sort       = $desc ? 'desc' : 'asc';
74
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->paginate($perPage, $columns);
75
    }
76
77
    /**
78
     * Count all records based on the given condition from storage.
79
     *
80
     * @param  array   $conditions array of conditions
81
     * @return collection
82
     */
83
    public function count($conditions = false)
84
    {
85 View Code Duplication
        if($conditions) {
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...
86
            $conditions = $this->constructConditions($conditions, $this->model);
87
            return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->count();
88
        }
89
        
90
        return $this->model->count();
91
    }
92
93
    /**
94
     * Pluck column based on the given condition from storage.
95
     *
96
     * @param  array   $conditions array of conditions
97
     * @param  string   $column
98
     * @return collection
99
     */
100
    public function pluck($conditions, $column)
101
    {
102
        $conditions = $this->constructConditions($conditions, $this->model);
103
        return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->pluck($column);
104
    }
105
    
106
    /**
107
     * Save the given model to the storage.
108
     *
109
     * @param  array $data
110
     * @return mixed
111
     */
112
    public function save(array $data)
113
    {
114
        $local = \Session::get('locale');
115
        \Session::put('locale', 'all');
116
        $model      = false;
117
        $relations  = [];
118
119
        \DB::transaction(function () use (&$model, &$relations, $data) {
120
            
121
            $model     = $this->prepareModel($data);
122
            $relations = $this->prepareRelations($data, $model);
123
            $model     = $this->saveModel($model, $relations);
124
        });
125
        \Session::put('locale', $local);
126
        
127
        if (count($relations)) {
128
            $model->load(...array_keys($relations));
0 ignored issues
show
Bug introduced by
The method load cannot be called on $model (of type false).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
129
        }
130
131
        return $model;
132
    }
133
    
134
    /**
135
     * Insert the given model/models to the storage.
136
     *
137
     * @param  array $data
138
     * @return mixed
139
     */
140
    public function insert(array $data)
141
    {
142
        return $this->model->insert($data);
143
    }
144
145
    /**
146
     * Delete record from the storage based on the given
147
     * condition.
148
     *
149
     * @param  var $value condition value
150
     * @param  string $attribute condition column name
151
     * @return void
152
     */
153
    public function delete($value, $attribute = 'id')
154
    {
155
        \DB::transaction(function () use ($value, $attribute) {
156
            $this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function ($model) {
157
                $model->delete();
158
            });
159
        });
160
    }
161
    
162
    /**
163
     * Fetch records from the storage based on the given
164
     * id.
165
     *
166
     * @param  integer $id
167
     * @param  string[]   $relations
168
     * @param  array   $columns
169
     * @return object
170
     */
171
    public function find($id, $relations = [], $columns = ['*'])
172
    {
173
        return $this->model->with($relations)->find($id, $columns);
174
    }
175
    
176
    /**
177
     * Fetch records from the storage based on the given
178
     * condition.
179
     *
180
     * @param  array   $conditions array of conditions
181
     * @param  array   $relations
182
     * @param  string  $sortBy
183
     * @param  boolean $desc
184
     * @param  array   $columns
185
     * @return collection
186
     */
187
    public function findBy($conditions, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = ['*'])
188
    {
189
        $conditions = $this->constructConditions($conditions, $this->model);
190
        $sort       = $desc ? 'desc' : 'asc';
191
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->get($columns);
192
    }
193
194
    /**
195
     * Fetch the first record from the storage based on the given
196
     * condition.
197
     *
198
     * @param  array   $conditions array of conditions
199
     * @param  array   $relations
200
     * @param  array   $columns
201
     * @return object
202
     */
203
    public function first($conditions, $relations = [], $columns = ['*'])
204
    {
205
        $conditions = $this->constructConditions($conditions, $this->model);
206
        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->first($columns);
207
    }
208
209
    /**
210
     * Return the deleted models in pages based on the given conditions.
211
     *
212
     * @param  array   $conditions array of conditions
213
     * @param  integer $perPage
214
     * @param  string  $sortBy
215
     * @param  boolean $desc
216
     * @param  array   $columns
217
     * @return collection
218
     */
219
    public function deleted($conditions, $perPage = 15, $sortBy = 'created_at', $desc = 1, $columns = ['*'])
220
    {
221
        unset($conditions['page']);
222
        unset($conditions['perPage']);
223
        unset($conditions['sortBy']);
224
        unset($conditions['sort']);
225
        $conditions = $this->constructConditions($conditions, $this->model);
226
        $sort       = $desc ? 'desc' : 'asc';
227
        $model      = $this->model->onlyTrashed();
228
229
        if (count($conditions['conditionValues'])) {
230
            $model->whereRaw($conditions['conditionString'], $conditions['conditionValues']);
231
        }
232
233
        return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
234
    }
235
236
    /**
237
     * Restore the deleted model.
238
     *
239
     * @param  integer $id
240
     * @return void
241
     */
242
    public function restore($id)
243
    {
244
        $model = $this->model->onlyTrashed()->find($id);
245
246
        if (! $model) {
247
            \Errors::notFound(class_basename($this->model).' with id : '.$id);
248
        }
249
250
        $model->restore();
251
    }
252
253
    /**
254
     * Fill the model with the given data.
255
     *
256
     * @param   array  $data
257
     *
258
     * @return  object
259
     */
260
    public function prepareModel($data)
261
    {
262
        $modelClass = $this->model;
263
264
        /**
265
         * If the id is present in the data then select the model for updating,
266
         * else create new model.
267
         * @var array
268
         */
269
        $model = Arr::has($data, 'id') ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
270
        if (! $model) {
271
            \Errors::notFound(class_basename($modelClass).' with id : '.$data['id']);
272
        }
273
274
        /**
275
         * Construct the model object with the given data,
276
         * and if there is a relation add it to relations array,
277
         * then save the model.
278
         */
279
        foreach ($data as $key => $value) {
280
            if (array_search($key, $model->getFillable(), true) !== false) {
281
                /**
282
                 * If the attribute isn't a relation and prevent attributes not in the fillable.
283
                 */
284
                $model->$key = $value;
285
            }
286
        }
287
288
        return $model;
289
    }
290
    
291
    /**
292
     * Prepare related models based on the given data for the given model.
293
     *
294
     * @param   array  $data
295
     * @param   object $model
296
     *
297
     * @return  array
298
     */
299
    public function prepareRelations($data, $model)
300
    {
301
        /**
302
         * Init the relation array
303
         *
304
         * @var array
305
         */
306
        $relations = [];
307
308
        /**
309
         * Construct the model object with the given data,
310
         * and if there is a relation add it to relations array,
311
         * then save the model.
312
         */
313
        foreach ($data as $key => $value) {
314
            /**
315
             * If the attribute is a relation.
316
             */
317
            $relation = \Str::camel($key);
318
            if (method_exists($model, $relation) && \Core::$relation()) {
319
                /**
320
                 * Check if the relation is a collection.
321
                 */
322
                if (class_basename($model->$relation) == 'Collection') {
323
                    /**
324
                     * If the relation has no value then marke the relation data
325
                     * related to the model to be deleted.
326
                     */
327
                    if (! $value || ! count($value)) {
328
                        $relations[$relation] = 'delete';
329
                    }
330
                }
331
                if (is_array($value)) {
332
                    /**
333
                     * Loop through the relation data.
334
                     */
335
                    foreach ($value as $attr => $val) {
336
                        /**
337
                         * Get the relation model.
338
                         */
339
                        $relationBaseModel = \Core::$relation()->model;
340
341
                        /**
342
                         * Check if the relation is a collection.
343
                         */
344
                        if (class_basename($model->$relation) == 'Collection') {
345
                            if (! is_array($val)) {
346
                                $relationModel = $relationBaseModel->lockForUpdate()->find($val);
347
                            } else {
348
                                /**
349
                                 * If the id is present in the data then select the relation model for updating,
350
                                 * else create new model.
351
                                 */
352
                                $relationModel = Arr::has($val, 'id') ? $relationBaseModel->lockForUpdate()->find($val['id']) : new $relationBaseModel;
353
                            }
354
355
                            /**
356
                             * If model doesn't exists.
357
                             */
358
                            if (! $relationModel) {
359
                                \Errors::notFound(class_basename($relationBaseModel).' with id : '.$val['id']);
360
                            }
361
362
                            if (is_array($val)) {
363
                                /**
364
                                 * Loop through the relation attributes.
365
                                 */
366
                                foreach ($val as $attr => $val) {
367
                                    /**
368
                                     * Prevent the sub relations or attributes not in the fillable.
369
                                     */
370
                                    if (gettype($val) !== 'object' && gettype($val) !== 'array' && array_search($attr, $relationModel->getFillable(), true) !== false) {
371
                                        $relationModel->$attr = $val;
372
                                    } elseif (gettype($val) !== 'object' && gettype($val) !== 'array' && $attr !== 'id') {
373
                                        $extra[$attr] = $val;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$extra was never initialized. Although not strictly required by PHP, it is generally a good practice to add $extra = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
374
                                    }
375
                                }
376
                            }
377
378
                            if (isset($extra)) {
379
                                $relationModel->extra = $extra;
380
                            }
381
                            $relations[$relation][] = $relationModel;
382
                        } else {
383
                            /**
384
                             * Prevent the sub relations.
385
                             */
386
                            if (gettype($val) !== 'object' && gettype($val) !== 'array') {
387
                                /**
388
                                 * If the id is present in the data then select the relation model for updating,
389
                                 * else create new model.
390
                                 */
391
                                $relationModel = Arr::has($value, 'id') ? $relationBaseModel->lockForUpdate()->find($value['id']) : new $relationBaseModel;
392
393
                                /**
394
                                 * If model doesn't exists.
395
                                 */
396
                                if (! $relationModel) {
397
                                    \Errors::notFound(class_basename($relationBaseModel).' with id : '.$value['id']);
398
                                }
399
400
                                foreach ($value as $relationAttribute => $relationValue) {
401
                                    /**
402
                                     * Prevent attributes not in the fillable.
403
                                     */
404
                                    if (array_search($relationAttribute, $relationModel->getFillable(), true) !== false) {
405
                                        $relationModel->$relationAttribute = $relationValue;
406
                                    }
407
                                }
408
409
                                $relations[$relation] = $relationModel;
410
                            }
411
                        }
412
                    }
413
                }
414
            }
415
        }
416
417
        return $relations;
418
    }
419
420
    /**
421
     * Save the model with related models.
422
     *
423
     * @param   object  $model
424
     * @param   array   $relations
425
     *
426
     * @return  object
427
     */
428
    public function saveModel($model, $relations)
429
    {
430
431
        /**
432
         * Loop through the relations array.
433
         */
434
        foreach ($relations as $key => $value) {
435
            /**
436
             * If the relation is marked for delete then delete it.
437
             */
438
            if ($value == 'delete' && $model->$key()->count()) {
439
                $model->$key()->delete();
440
            } elseif (gettype($value) == 'array') {
441
                /**
442
                 * Save the model.
443
                 */
444
                $model->save();
445
                $ids = [];
446
447
                /**
448
                 * Loop through the relations.
449
                 */
450
                foreach ($value as $val) {
451
                    switch (class_basename($model->$key())) {
452
                        /**
453
                         * If the relation is one to many then update it's foreign key with
454
                         * the model id and save it then add its id to ids array to delete all
455
                         * relations who's id isn't in the ids array.
456
                         */
457
                        case 'HasMany':
458
                            $foreignKeyName       = $model->$key()->getForeignKeyName();
459
                            $val->$foreignKeyName = $model->id;
460
                            $val->save();
461
                            $ids[] = $val->id;
462
                            break;
463
464
                        /**
465
                         * If the relation is many to many then add it's id to the ids array to
466
                         * attache these ids to the model.
467
                         */
468
                        case 'BelongsToMany':
469
                            $extra = $val->extra;
470
                            unset($val->extra);
471
                            $val->save();
472
                            $ids[$val->id] = $extra ?? [];
473
                            break;
474
                    }
475
                }
476
                switch (class_basename($model->$key())) {
477
                    /**
478
                     * If the relation is one to many then delete all
479
                     * relations who's id isn't in the ids array.
480
                     */
481
                    case 'HasMany':
482
                        $model->$key()->whereNotIn('id', $ids)->delete();
483
                        break;
484
485
                    /**
486
                     * If the relation is many to many then
487
                     * detach the previous data and attach
488
                     * the ids array to the model.
489
                     */
490
                    case 'BelongsToMany':
491
                        $model->$key()->detach();
492
                        $model->$key()->attach($ids);
493
                        break;
494
                }
495
            } else {
496
                switch (class_basename($model->$key())) {
497
                    /**
498
                     * If the relation is one to one.
499
                     */
500
                    case 'HasOne':
501
                        /**
502
                         * Save the model.
503
                         */
504
                        $model->save();
505
                        $foreignKeyName         = $model->$key()->getForeignKeyName();
506
                        $value->$foreignKeyName = $model->id;
507
                        $value->save();
508
                        break;
509
                    case 'BelongsTo':
510
                        /**
511
                         * Save the model.
512
                         */
513
                        $value->save();
514
                        $model->$key()->associate($value);
515
                        break;
516
                }
517
            }
518
        }
519
520
        /**
521
         * Save the model.
522
         */
523
        $model->save();
524
525
        return $model;
526
    }
527
528
    /**
529
     * Build the conditions recursively for the retrieving methods.
530
     * @param  array $conditions
531
     * @return array
532
     */
533
    protected function constructConditions($conditions, $model)
534
    {
535
        $conditionString = '';
536
        $conditionValues = [];
537
        foreach ($conditions as $key => $value) {
538
            if (Str::contains($key, '->')) {
539
                $key = $this->wrapJsonSelector($key);
540
            }
541
542
            if ($key == 'and') {
543
                $conditions       = $this->constructConditions($value, $model);
544
                $conditionString .= str_replace('{op}', 'and', $conditions['conditionString']).' {op} ';
545
                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
546
            } elseif ($key == 'or') {
547
                $conditions       = $this->constructConditions($value, $model);
548
                $conditionString .= str_replace('{op}', 'or', $conditions['conditionString']).' {op} ';
549
                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
550
            } else {
551
                if (is_array($value)) {
552
                    $operator = $value['op'];
553
                    if (strtolower($operator) == 'between') {
554
                        $value1 = $value['val1'];
555
                        $value2 = $value['val2'];
556
                    } else {
557
                        $value = Arr::get($value, 'val', '');
558
                    }
559
                } else {
560
                    $operator = '=';
561
                }
562
                
563
                if (strtolower($operator) == 'between') {
564
                    $conditionString  .= $key.' >= ? and ';
565
                    $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...
566
567
                    $conditionString  .= $key.' <= ? {op} ';
568
                    $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...
569
                } elseif (strtolower($operator) == 'in') {
570
                    $conditionValues  = array_merge($conditionValues, $value);
571
                    $inBindingsString = rtrim(str_repeat('?,', count($value)), ',');
572
                    $conditionString .= $key.' in ('.rtrim($inBindingsString, ',').') {op} ';
573
                } elseif (strtolower($operator) == 'null') {
574
                    $conditionString .= $key.' is null {op} ';
575
                } elseif (strtolower($operator) == 'not null') {
576
                    $conditionString .= $key.' is not null {op} ';
577
                } elseif (strtolower($operator) == 'has') {
578
                    $sql              = $model->withTrashed()->withoutGlobalScopes()->has($key)->toSql();
579
                    $bindings         = $model->withTrashed()->withoutGlobalScopes()->has($key)->getBindings();
580
                    $conditions       = $this->constructConditions($value, $model->$key()->getRelated());
581
                    $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1).' and '.$conditions['conditionString'].') {op} ';
582
                    $conditionValues  = array_merge($conditionValues, $bindings);
583
                    $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
584
                } else {
585
                    $conditionString  .= $key.' '.$operator.' ? {op} ';
586
                    $conditionValues[] = $value;
587
                }
588
            }
589
        }
590
        $conditionString = '('.rtrim($conditionString, '{op} ').')';
591
        return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
592
    }
593
594
    /**
595
     * Wrap the given JSON selector.
596
     *
597
     * @param  string  $value
598
     * @return string
599
     */
600
    protected function wrapJsonSelector($value)
601
    {
602
        $removeLast = strpos($value, ')');
603
        $value      = $removeLast === false ? $value : substr($value, 0, $removeLast);
604
        $path       = explode('->', $value);
605
        $field      = array_shift($path);
606
        $result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
607
            return '"'.$part.'"';
608
        })->implode('.'));
609
        
610
        return $removeLast === false ? $result : $result.')';
611
    }
612
}
613