Passed
Push — master ( 5a9c7e...e00e24 )
by Nate
04:01 queued 01:22
created

ModelAccessor   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 709
Duplicated Lines 29.2 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 69
lcom 1
cbo 8
dl 207
loc 709
rs 5
c 0
b 0
f 0

34 Methods

Rating   Name   Duplication   Size   Complexity  
modelClass() 0 1 ?
A modelClassInstance() 0 4 1
A create() 23 23 3
A createFromRecord() 0 19 3
B findAll() 24 24 4
A getAll() 0 12 2
B find() 0 26 5
A get() 0 13 2
B findById() 25 25 3
A getById() 13 13 2
A freshFindById() 14 14 2
A freshGetById() 12 12 2
A findAllByQuery() 12 12 2
A findByQuery() 11 11 2
A findAllByCondition() 0 17 3
A getAllByCondition() 0 12 2
A findByCondition() 11 11 2
A getByCondition() 0 12 2
A findAllByCriteria() 18 18 3
A getAllByCriteria() 0 12 2
A findByCriteria() 11 11 2
A getByCriteria() 0 12 2
A findByRecord() 17 17 2
A getByRecord() 0 4 1
A findCache() 16 16 3
A findCacheById() 0 13 2
A isCachedById() 0 4 1
A cacheById() 0 14 2
A findCacheByRecord() 0 12 2
A addToCache() 0 7 1
A notFoundException() 0 10 1
A notFoundByIdException() 0 11 1
A notFoundByCriteriaException() 0 11 1
A notFoundByConditionException() 0 11 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ModelAccessor 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 ModelAccessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package    Spark
5
 * @author     Flipbox Factory <[email protected]>
6
 * @copyright  2010-2016 Flipbox Digital Limited
7
 * @license    https://github.com/FlipboxFactory/Craft3-Spark/blob/master/LICENSE
8
 * @link       https://github.com/FlipboxFactory/Craft3-Spark
9
 * @since      Class available since Release 1.0.0
10
 */
11
12
namespace flipbox\spark\services;
13
14
use craft\helpers\Json as JsonHelper;
15
use flipbox\spark\exceptions\ModelNotFoundException;
16
use flipbox\spark\helpers\ArrayHelper;
17
use flipbox\spark\helpers\ModelHelper;
18
use flipbox\spark\models\Model;
19
use flipbox\spark\records\Record;
20
use flipbox\spark\services\traits\ModelRecordBehavior;
21
use yii\base\Component;
22
use yii\base\InvalidConfigException;
23
use yii\db\QueryInterface;
24
25
abstract class ModelAccessor extends Component
26
{
27
28
    use ModelRecordBehavior;
29
30
    /**
31
     * @var Model[]
32
     */
33
    protected $_cacheAll;
34
35
    /**
36
     * @var Model[]
37
     */
38
    protected $_cacheById = [];
39
40
41
    /*******************************************
42
     * MODEL CLASSES
43
     *******************************************/
44
45
    /**
46
     * @return string
47
     */
48
    public abstract static function modelClass(): string;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
49
50
    /**
51
     * @return string
52
     */
53
    public static function modelClassInstance(): string
54
    {
55
        return Model::class;
56
    }
57
58
    /*******************************************
59
     * CREATE
60
     *******************************************/
61
62
    /**
63
     * @param array $config
64
     * @param string|null $toScenario
65
     * @throws InvalidConfigException
66
     * @return Model
67
     */
68 View Code Duplication
    public function create($config = [], string $toScenario = null): Model
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...
69
    {
70
71
        // Treat records as known data and set via config
72
        if ($config instanceof Record) {
73
            return $this->createFromRecord($config, $toScenario);
74
        }
75
76
        // Force Array
77
        if (!is_array($config)) {
78
            $config = ArrayHelper::toArray($config, [], false);
79
        }
80
81
        // Set the model class
82
        $config['class'] = static::modelClass();
83
84
        return ModelHelper::create(
85
            $config,
86
            static::modelClassInstance(),
87
            $toScenario
88
        );
89
90
    }
91
92
    /**
93
     * @param Record $record
94
     * @param string|null $toScenario
95
     * @throws InvalidConfigException
96
     * @return Model
97
     */
98
    protected function createFromRecord(Record $record, string $toScenario = null): Model
99
    {
100
101
        if (null !== $toScenario) {
102
            $record->setScenario($toScenario);
103
        }
104
105
        $modelClass = static::modelClass();
106
107
        /** @var Model $model */
108
        $model = new $modelClass($record);
109
110
        if (null !== $toScenario) {
111
            $model->setScenario($toScenario);
112
        }
113
114
        return $model;
115
116
    }
117
118
    /*******************************************
119
     * FIND/GET ALL
120
     *******************************************/
121
122
    /**
123
     * @param string $toScenario
124
     * @return Model[]
125
     */
126 View Code Duplication
    public function findAll(string $toScenario = null)
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...
127
    {
128
129
        // Check addToCache
130
        if (is_null($this->_cacheAll)) {
131
132
            $this->_cacheAll = [];
133
134
            // Find record in db
135
            if ($records = $this->findAllRecords()) {
136
137
                foreach ($records as $record) {
138
139
                    $this->_cacheAll[] = $this->findByRecord($record, $toScenario);
140
141
                }
142
143
            }
144
145
        }
146
147
        return $this->_cacheAll;
148
149
    }
150
151
    /**
152
     * @param string $toScenario
153
     * @return Model[]
154
     * @throws ModelNotFoundException
155
     */
156
    public function getAll(string $toScenario = null): array
157
    {
158
159
        if (!$models = $this->findAll($toScenario)) {
160
161
            $this->notFoundException();
162
163
        }
164
165
        return $models;
166
167
    }
168
169
    /*******************************************
170
     * FIND/GET
171
     *******************************************/
172
173
    /**
174
     * @param $identifier
175
     * @param string $toScenario
176
     * @return Model|null
177
     */
178
    public function find($identifier, string $toScenario = null)
179
    {
180
181
        if ($identifier instanceof Model) {
182
183
            $this->addToCache($identifier);
184
185
            if (null !== $toScenario) {
186
                $identifier->setScenario($toScenario);
187
            }
188
189
            return $identifier;
190
191
        } elseif ($identifier instanceof Record) {
192
193
            return $this->findByRecord($identifier, $toScenario);
194
195
        } elseif (is_numeric($identifier)) {
196
197
            return $this->findById($identifier, $toScenario);
198
199
        }
200
201
        return null;
202
203
    }
204
205
    /**
206
     * @param $identifier
207
     * @param string $toScenario
208
     * @return Model
209
     * @throws ModelNotFoundException
210
     */
211
    public function get($identifier, string $toScenario = null): Model
212
    {
213
214
        // Find model by ID
215
        if (!$model = $this->find($identifier, $toScenario)) {
216
217
            $this->notFoundException();
218
219
        }
220
221
        return $model;
222
223
    }
224
225
    /*******************************************
226
     * FIND/GET BY ID
227
     *******************************************/
228
229
    /**
230
     * @param int $id
231
     * @param string|null $toScenario
232
     * @return Model|null
233
     */
234 View Code Duplication
    public function findById(int $id, string $toScenario = null)
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...
235
    {
236
237
        // Check cache
238
        if (!$model = $this->findCacheById($id)) {
239
240
            // Find record in db
241
            if ($record = $this->findRecordById($id)) {
242
243
                // Perhaps in cache
244
                $model = $this->findByRecord($record, $toScenario);
245
246
            } else {
247
248
                $this->_cacheById[$id] = null;
249
250
                return null;
251
252
            }
253
254
        }
255
256
        return $model;
257
258
    }
259
260
    /**
261
     * @param int $id
262
     * @param string|null $toScenario
263
     * @return Model
264
     * @throws ModelNotFoundException
265
     */
266 View Code Duplication
    public function getById(int $id, string $toScenario = null): Model
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...
267
    {
268
269
        // Find by ID
270
        if (!$model = $this->findById($id, $toScenario)) {
271
272
            $this->notFoundByIdException($id);
273
274
        }
275
276
        return $model;
277
278
    }
279
280
    /**
281
     * @param int $id
282
     * @param string|null $toScenario
283
     * @return Model|null
284
     */
285 View Code Duplication
    public function freshFindById(int $id, string $toScenario = null)
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...
286
    {
287
288
        // Find record in db
289
        if ($record = $this->findRecordById($id)) {
290
291
            // Create
292
            return $this->createFromRecord($record, $toScenario);
293
294
        }
295
296
        return null;
297
298
    }
299
300
    /**
301
     * @param int $id
302
     * @param string|null $toScenario
303
     * @return Model
304
     * @throws ModelNotFoundException
305
     */
306 View Code Duplication
    public function freshGetById(int $id, string $toScenario = null): Model
1 ignored issue
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...
307
    {
308
309
        if (!$model = $this->freshFindById($id, $toScenario)) {
310
311
            $this->notFoundByIdException($id);
312
313
        }
314
315
        return $model;
316
317
    }
318
319
    /*******************************************
320
     * FIND/GET BY QUERY
321
     *******************************************/
322
323
    /**
324
     * @param QueryInterface $query
325
     * @param string $toScenario
326
     * @return Model[]
327
     */
328 View Code Duplication
    public function findAllByQuery(QueryInterface $query, string $toScenario = null): array
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...
329
    {
330
331
        $models = array();
332
333
        foreach ($query->all() as $record) {
334
            $models[] = $this->findByRecord($record, $toScenario);
335
        }
336
337
        return $models;
338
339
    }
340
341
    /**
342
     * @param QueryInterface $query
343
     * @param string $toScenario
344
     * @return Model|null
345
     */
346 View Code Duplication
    public function findByQuery(QueryInterface $query, string $toScenario = null)
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...
347
    {
348
349
        /** @var Record $record */
350
        if (!$record = $query->one()) {
351
            return null;
352
        }
353
354
        return $this->findByRecord($record, $toScenario);
0 ignored issues
show
Documentation introduced by
$record is of type array|boolean, but the function expects a object<flipbox\spark\records\Record>.

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...
355
356
    }
357
358
    /*******************************************
359
     * FIND/GET BY CONDITION
360
     *******************************************/
361
362
    /**
363
     * @param $condition
364
     * @param string $toScenario
365
     * @return Model[]
366
     */
367
    public function findAllByCondition($condition, string $toScenario = null): array
368
    {
369
370
        $models = [];
371
372
        // Find record in db
373
        if ($records = $this->findAllRecordsByCondition($condition)) {
374
375
            foreach ($records as $record) {
376
                $models[] = $this->findByRecord($record, $toScenario);
377
            }
378
379
        }
380
381
        return $models;
382
383
    }
384
385
    /**
386
     * @param $condition
387
     * @param string $toScenario
388
     * @return Model[]
389
     * @throws ModelNotFoundException
390
     */
391
    public function getAllByCondition($condition, string $toScenario = null): array
392
    {
393
394
        if (!$models = $this->findAllByCondition($condition, $toScenario)) {
395
396
            $this->notFoundByConditionException($condition);
397
398
        }
399
400
        return $models;
401
402
    }
403
404
    /**
405
     * @param $condition
406
     * @param string $toScenario
407
     * @return Model|null
408
     */
409 View Code Duplication
    public function findByCondition($condition, string $toScenario = null)
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...
410
    {
411
412
        // Find record in db
413
        if ($record = $this->findRecordByCondition($condition)) {
414
            return $this->findByRecord($record, $toScenario);
415
        }
416
417
        return null;
418
419
    }
420
421
    /**
422
     * @param $condition
423
     * @param string $toScenario
424
     * @return Model
425
     * @throws ModelNotFoundException
426
     */
427
    public function getByCondition($condition, string $toScenario = null): Model
428
    {
429
430
        if (!$model = $this->findByCondition($condition, $toScenario)) {
431
432
            $this->notFoundByConditionException($condition);
433
434
        }
435
436
        return $model;
437
438
    }
439
440
    /*******************************************
441
     * FIND/GET BY CRITERIA
442
     *******************************************/
443
444
    /**
445
     * @param $criteria
446
     * @param string $toScenario
447
     * @return Model[]
448
     */
449 View Code Duplication
    public function findAllByCriteria($criteria, string $toScenario = null): array
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...
450
    {
451
452
        $models = [];
453
454
        // Find record in db
455
        if ($records = $this->findAllRecordsByCriteria($criteria)
456
        ) {
457
458
            foreach ($records as $record) {
459
                $models[] = $this->findByRecord($record, $toScenario);
460
            }
461
462
        }
463
464
        return $models;
465
466
    }
467
468
    /**
469
     * @param $criteria
470
     * @param string $toScenario
471
     * @return Model[]
472
     * @throws ModelNotFoundException
473
     */
474
    public function getAllByCriteria($criteria, string $toScenario = null): array
475
    {
476
477
        if (!$models = $this->findAllByCriteria($criteria, $toScenario)) {
478
479
            $this->notFoundByCriteriaException($criteria);
480
481
        }
482
483
        return $models;
484
485
    }
486
487
    /**
488
     * @param $criteria
489
     * @param string $toScenario
490
     * @return Model|null
491
     */
492 View Code Duplication
    public function findByCriteria($criteria, string $toScenario = null)
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...
493
    {
494
495
        // Find record in db
496
        if ($record = $this->findRecordByCriteria($criteria)) {
497
            return $this->findByRecord($record, $toScenario);
498
        }
499
500
        return null;
501
502
    }
503
504
    /**
505
     * @param $criteria
506
     * @param string $toScenario
507
     * @return Model
508
     * @throws ModelNotFoundException
509
     */
510
    public function getByCriteria($criteria, string $toScenario = null): Model
511
    {
512
513
        if (!$model = $this->findByCriteria($criteria, $toScenario)) {
514
515
            $this->notFoundByCriteriaException($criteria);
516
517
        }
518
519
        return $model;
520
521
    }
522
523
524
    /*******************************************
525
     * FIND/GET BY RECORD
526
     *******************************************/
527
528
    /**
529
     * @param Record $record
530
     * @param string $toScenario
531
     * @return Model
532
     */
533 View Code Duplication
    public function findByRecord(Record $record, string $toScenario = null): Model
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...
534
    {
535
536
        // Check addToCache
537
        if (!$model = $this->findCacheByRecord($record)) {
538
539
            // New model
540
            $model = $this->createFromRecord($record, $toScenario);
541
542
            // Cache it
543
            $this->addToCache($model);
544
545
        }
546
547
        return $model;
548
549
    }
550
551
    /**
552
     * @param Record $record
553
     * @param string $toScenario
554
     * @return Model
555
     */
556
    public function getByRecord(Record $record, string $toScenario = null): Model
557
    {
558
        return $this->findByRecord($record, $toScenario);
559
    }
560
561
562
    /*******************************************
563
     * CACHE
564
     *******************************************/
565
566
    /**
567
     * @param $identifier
568
     * @return Model|null
569
     */
570 View Code Duplication
    public function findCache($identifier)
1 ignored issue
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...
571
    {
572
573
        if ($identifier instanceof Record) {
574
575
            return $this->findCacheByRecord($identifier);
576
577
        } elseif (is_numeric($identifier)) {
578
579
            return $this->findCacheById($identifier);
580
581
        }
582
583
        return null;
584
585
    }
586
587
    /**
588
     * Find an existing cache by ID
589
     *
590
     * @param $id
591
     * @return Model|null
592
     */
593
    public function findCacheById(int $id)
594
    {
595
596
        // Check if already in addToCache
597
        if ($this->isCachedById($id)) {
598
599
            return $this->_cacheById[$id];
600
601
        }
602
603
        return null;
604
605
    }
606
607
    /**
608
     * Identify whether in cache by ID
609
     *
610
     * @param $id
611
     * @return bool
612
     */
613
    protected function isCachedById(int $id)
614
    {
615
        return array_key_exists($id, $this->_cacheById);
616
    }
617
618
    /**
619
     * @param Model $model
620
     * @return $this
621
     */
622
    protected function cacheById(Model $model)
623
    {
624
625
        // Check if already in cache
626
        if (!$id = $this->isCachedById($model->id)) {
627
628
            // Cache it
629
            $this->_cacheById[$id] = $model;
630
631
        }
632
633
        return $this;
634
635
    }
636
637
    /**
638
     * @param Record $record
639
     * @return Model|null
640
     */
641
    public function findCacheByRecord(Record $record)
642
    {
643
644
        // Check if already in addToCache by id
645
        if ($id = $this->isCachedById($record->id)) {
646
647
            return $this->findCacheById($id);
0 ignored issues
show
Documentation introduced by
$id 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...
648
649
        }
650
651
        return null;
652
    }
653
654
    /**
655
     * @param Model $model
656
     * @return static
657
     */
658
    public function addToCache(Model $model)
659
    {
660
661
        $this->cacheById($model);
662
663
        return $this;
664
    }
665
666
667
    /*******************************************
668
     * EXCEPTIONS
669
     *******************************************/
670
671
    /**
672
     * @throws ModelNotFoundException
673
     */
674
    protected function notFoundException()
675
    {
676
677
        throw new ModelNotFoundException(
678
            sprintf(
679
                "Model does not exist."
680
            )
681
        );
682
683
    }
684
685
    /**
686
     * @param int|null $id
687
     * @throws ModelNotFoundException
688
     */
689
    protected function notFoundByIdException(int $id = null)
690
    {
691
692
        throw new ModelNotFoundException(
693
            sprintf(
694
                'Model does not exist with the id "%s".',
695
                (string)$id
696
            )
697
        );
698
699
    }
700
701
    /**
702
     * @param null $criteria
703
     * @throws ModelNotFoundException
704
     */
705
    protected function notFoundByCriteriaException($criteria = null)
706
    {
707
708
        throw new ModelNotFoundException(
709
            sprintf(
710
                'Model does not exist with the criteria "%s".',
711
                (string)JsonHelper::encode($criteria)
712
            )
713
        );
714
715
    }
716
717
    /**
718
     * @param null $condition
719
     * @throws ModelNotFoundException
720
     */
721
    protected function notFoundByConditionException($condition = null)
722
    {
723
724
        throw new ModelNotFoundException(
725
            sprintf(
726
                'Model does not exist with the condition "%s".',
727
                (string)JsonHelper::encode($condition)
728
            )
729
        );
730
731
    }
732
733
}
734