Passed
Push — master ( e046f3...04364f )
by Nate
02:43
created

Model::findRecordById()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipbox/spark/blob/master/LICENSE
6
 * @link       https://github.com/flipbox/spark
7
 */
8
9
namespace flipbox\spark\services;
10
11
use craft\helpers\Json as JsonHelper;
12
use flipbox\spark\exceptions\ModelNotFoundException;
13
use flipbox\spark\helpers\ArrayHelper;
14
use flipbox\spark\helpers\ModelHelper;
15
use flipbox\spark\models\Model as BaseModel;
16
use flipbox\spark\records\Record;
17
use flipbox\spark\services\traits\ModelRecordBehavior;
18
use yii\base\Component;
19
use yii\base\InvalidConfigException;
20
use yii\db\QueryInterface;
21
22
/**
23
 * @package flipbox\spark\services
24
 * @author Flipbox Factory <[email protected]>
25
 * @since 1.0.0
26
 */
27
abstract class Model extends Component
28
{
29
30
    use ModelRecordBehavior;
31
32
    /**
33
     * @var BaseModel[]
34
     */
35
    protected $_cacheAll;
36
37
    /**
38
     * @var BaseModel[]
39
     */
40
    protected $_cacheById = [];
41
42
43
    /*******************************************
44
     * MODEL CLASSES
45
     *******************************************/
46
47
    /**
48
     * @return string
49
     */
50
    public abstract static function modelClass(): string;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
51
52
    /**
53
     * @return string
54
     */
55
    public static function modelClassInstance(): string
56
    {
57
        return BaseModel::class;
58
    }
59
60
    /*******************************************
61
     * CREATE
62
     *******************************************/
63
64
    /**
65
     * @param array $config
66
     * @param string|null $toScenario
67
     * @throws InvalidConfigException
68
     * @return BaseModel
69
     */
70 View Code Duplication
    public function create($config = [], string $toScenario = null): BaseModel
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...
71
    {
72
73
        // Treat records as known data and set via config
74
        if ($config instanceof Record) {
75
            return $this->createFromRecord($config, $toScenario);
76
        }
77
78
        // Force Array
79
        if (!is_array($config)) {
80
            $config = ArrayHelper::toArray($config, [], false);
81
        }
82
83
        // Set the model class
84
        $config['class'] = static::modelClass();
85
86
        return ModelHelper::create(
87
            $config,
88
            static::modelClassInstance(),
89
            $toScenario
90
        );
91
92
    }
93
94
    /**
95
     * @param Record $record
96
     * @param string|null $toScenario
97
     * @throws InvalidConfigException
98
     * @return BaseModel
99
     */
100
    protected function createFromRecord(Record $record, string $toScenario = null): BaseModel
101
    {
102
103
        if (null !== $toScenario) {
104
            $record->setScenario($toScenario);
105
        }
106
107
        $modelClass = static::modelClass();
108
109
        /** @var BaseModel $model */
110
        $model = new $modelClass($record);
111
112
        if (null !== $toScenario) {
113
            $model->setScenario($toScenario);
114
        }
115
116
        return $model;
117
118
    }
119
120
    /*******************************************
121
     * FIND/GET ALL
122
     *******************************************/
123
124
    /**
125
     * @param string $toScenario
126
     * @return BaseModel[]
127
     */
128 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...
129
    {
130
131
        // Check addToCache
132
        if (is_null($this->_cacheAll)) {
133
134
            $this->_cacheAll = [];
135
136
            // Find record in db
137
            if ($records = $this->findAllRecords()) {
138
139
                foreach ($records as $record) {
140
141
                    $this->_cacheAll[] = $this->findByRecord($record, $toScenario);
142
143
                }
144
145
            }
146
147
        }
148
149
        return $this->_cacheAll;
150
151
    }
152
153
    /**
154
     * @param string $toScenario
155
     * @return BaseModel[]
156
     * @throws ModelNotFoundException
157
     */
158
    public function getAll(string $toScenario = null): array
159
    {
160
161
        if (!$models = $this->findAll($toScenario)) {
162
163
            $this->notFoundException();
164
165
        }
166
167
        return $models;
168
169
    }
170
171
    /*******************************************
172
     * FIND/GET
173
     *******************************************/
174
175
    /**
176
     * @param $identifier
177
     * @param string $toScenario
178
     * @return BaseModel|null
179
     */
180
    public function find($identifier, string $toScenario = null)
181
    {
182
183
        if ($identifier instanceof BaseModel) {
184
185
            $this->addToCache($identifier);
186
187
            if (null !== $toScenario) {
188
                $identifier->setScenario($toScenario);
189
            }
190
191
            return $identifier;
192
193
        } elseif ($identifier instanceof Record) {
194
195
            return $this->findByRecord($identifier, $toScenario);
196
197
        } elseif (is_numeric($identifier)) {
198
199
            return $this->findById($identifier, $toScenario);
200
201
        }
202
203
        return null;
204
205
    }
206
207
    /**
208
     * @param $identifier
209
     * @param string $toScenario
210
     * @return BaseModel
211
     * @throws ModelNotFoundException
212
     */
213
    public function get($identifier, string $toScenario = null): BaseModel
214
    {
215
216
        // Find model by ID
217
        if (!$model = $this->find($identifier, $toScenario)) {
218
219
            $this->notFoundException();
220
221
        }
222
223
        return $model;
224
225
    }
226
227
    /*******************************************
228
     * FIND/GET BY ID
229
     *******************************************/
230
231
    /**
232
     * @param int $id
233
     * @param string|null $toScenario
234
     * @return BaseModel|null
235
     */
236 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...
237
    {
238
239
        // Check cache
240
        if (!$model = $this->findCacheById($id)) {
241
242
            // Find record in db
243
            if ($record = $this->findRecordById($id)) {
244
245
                // Perhaps in cache
246
                $model = $this->findByRecord($record, $toScenario);
247
248
            } else {
249
250
                $this->_cacheById[$id] = null;
251
252
                return null;
253
254
            }
255
256
        }
257
258
        return $model;
259
260
    }
261
262
    /**
263
     * @param int $id
264
     * @param string|null $toScenario
265
     * @return BaseModel
266
     * @throws ModelNotFoundException
267
     */
268 View Code Duplication
    public function getById(int $id, string $toScenario = null): BaseModel
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...
269
    {
270
271
        // Find by ID
272
        if (!$model = $this->findById($id, $toScenario)) {
273
274
            $this->notFoundByIdException($id);
275
276
        }
277
278
        return $model;
279
280
    }
281
282
    /**
283
     * @param int $id
284
     * @param string|null $toScenario
285
     * @return BaseModel|null
286
     */
287
    public function freshFindById(int $id, string $toScenario = null)
288
    {
289
290
        // Find record in db
291
        if ($record = $this->findRecordById($id)) {
292
293
            // Create
294
            return $this->createFromRecord($record, $toScenario);
295
296
        }
297
298
        return null;
299
300
    }
301
302
    /**
303
     * @param int $id
304
     * @param string|null $toScenario
305
     * @return BaseModel
306
     * @throws ModelNotFoundException
307
     */
308 View Code Duplication
    public function freshGetById(int $id, string $toScenario = null): BaseModel
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...
309
    {
310
311
        if (!$model = $this->freshFindById($id, $toScenario)) {
312
313
            $this->notFoundByIdException($id);
314
315
        }
316
317
        return $model;
318
319
    }
320
321
    /*******************************************
322
     * FIND/GET BY QUERY
323
     *******************************************/
324
325
    /**
326
     * @param QueryInterface $query
327
     * @param string $toScenario
328
     * @return BaseModel[]
329
     */
330 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...
331
    {
332
333
        $models = array();
334
335
        foreach ($query->all() as $record) {
336
            $models[] = $this->findByRecord($record, $toScenario);
337
        }
338
339
        return $models;
340
341
    }
342
343
    /**
344
     * @param QueryInterface $query
345
     * @param string $toScenario
346
     * @return BaseModel|null
347
     */
348 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...
349
    {
350
351
        /** @var Record $record */
352
        if (!$record = $query->one()) {
353
            return null;
354
        }
355
356
        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...
357
358
    }
359
360
    /*******************************************
361
     * FIND/GET BY CONDITION
362
     *******************************************/
363
364
    /**
365
     * @param $condition
366
     * @param string $toScenario
367
     * @return BaseModel[]
368
     */
369
    public function findAllByCondition($condition, string $toScenario = null): array
370
    {
371
372
        $models = [];
373
374
        // Find record in db
375
        if ($records = $this->findAllRecordsByCondition($condition)) {
376
377
            foreach ($records as $record) {
378
                $models[] = $this->findByRecord($record, $toScenario);
379
            }
380
381
        }
382
383
        return $models;
384
385
    }
386
387
    /**
388
     * @param $condition
389
     * @param string $toScenario
390
     * @return BaseModel[]
391
     * @throws ModelNotFoundException
392
     */
393
    public function getAllByCondition($condition, string $toScenario = null): array
394
    {
395
396
        if (!$models = $this->findAllByCondition($condition, $toScenario)) {
397
398
            $this->notFoundByConditionException($condition);
399
400
        }
401
402
        return $models;
403
404
    }
405
406
    /**
407
     * @param $condition
408
     * @param string $toScenario
409
     * @return BaseModel|null
410
     */
411 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...
412
    {
413
414
        // Find record in db
415
        if ($record = $this->findRecordByCondition($condition)) {
416
            return $this->findByRecord($record, $toScenario);
417
        }
418
419
        return null;
420
421
    }
422
423
    /**
424
     * @param $condition
425
     * @param string $toScenario
426
     * @return BaseModel
427
     * @throws ModelNotFoundException
428
     */
429
    public function getByCondition($condition, string $toScenario = null): BaseModel
430
    {
431
432
        if (!$model = $this->findByCondition($condition, $toScenario)) {
433
434
            $this->notFoundByConditionException($condition);
435
436
        }
437
438
        return $model;
439
440
    }
441
442
    /*******************************************
443
     * FIND/GET BY CRITERIA
444
     *******************************************/
445
446
    /**
447
     * @param $criteria
448
     * @param string $toScenario
449
     * @return BaseModel[]
450
     */
451 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...
452
    {
453
454
        $models = [];
455
456
        // Find record in db
457
        if ($records = $this->findAllRecordsByCriteria($criteria)
458
        ) {
459
460
            foreach ($records as $record) {
461
                $models[] = $this->findByRecord($record, $toScenario);
462
            }
463
464
        }
465
466
        return $models;
467
468
    }
469
470
    /**
471
     * @param $criteria
472
     * @param string $toScenario
473
     * @return BaseModel[]
474
     * @throws ModelNotFoundException
475
     */
476
    public function getAllByCriteria($criteria, string $toScenario = null): array
477
    {
478
479
        if (!$models = $this->findAllByCriteria($criteria, $toScenario)) {
480
481
            $this->notFoundByCriteriaException($criteria);
482
483
        }
484
485
        return $models;
486
487
    }
488
489
    /**
490
     * @param $criteria
491
     * @param string $toScenario
492
     * @return BaseModel|null
493
     */
494 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...
495
    {
496
497
        // Find record in db
498
        if ($record = $this->findRecordByCriteria($criteria)) {
499
            return $this->findByRecord($record, $toScenario);
500
        }
501
502
        return null;
503
504
    }
505
506
    /**
507
     * @param $criteria
508
     * @param string $toScenario
509
     * @return BaseModel
510
     * @throws ModelNotFoundException
511
     */
512
    public function getByCriteria($criteria, string $toScenario = null): BaseModel
513
    {
514
515
        if (!$model = $this->findByCriteria($criteria, $toScenario)) {
516
517
            $this->notFoundByCriteriaException($criteria);
518
519
        }
520
521
        return $model;
522
523
    }
524
525
526
    /*******************************************
527
     * FIND/GET BY RECORD
528
     *******************************************/
529
530
    /**
531
     * @param Record $record
532
     * @param string $toScenario
533
     * @return BaseModel
534
     */
535 View Code Duplication
    public function findByRecord(Record $record, string $toScenario = null): BaseModel
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...
536
    {
537
538
        // Check addToCache
539
        if (!$model = $this->findCacheByRecord($record)) {
540
541
            // New model
542
            $model = $this->createFromRecord($record, $toScenario);
543
544
            // Cache it
545
            $this->addToCache($model);
546
547
        }
548
549
        return $model;
550
551
    }
552
553
    /**
554
     * @param Record $record
555
     * @param string $toScenario
556
     * @return BaseModel
557
     */
558
    public function getByRecord(Record $record, string $toScenario = null): BaseModel
559
    {
560
        return $this->findByRecord($record, $toScenario);
561
    }
562
563
564
    /*******************************************
565
     * CACHE
566
     *******************************************/
567
568
    /**
569
     * @param $identifier
570
     * @return BaseModel|null
571
     */
572 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...
573
    {
574
575
        if ($identifier instanceof Record) {
576
577
            return $this->findCacheByRecord($identifier);
578
579
        } elseif (is_numeric($identifier)) {
580
581
            return $this->findCacheById($identifier);
582
583
        }
584
585
        return null;
586
587
    }
588
589
    /**
590
     * Find an existing cache by ID
591
     *
592
     * @param $id
593
     * @return BaseModel|null
594
     */
595
    public function findCacheById(int $id)
596
    {
597
598
        // Check if already in addToCache
599
        if ($this->isCachedById($id)) {
600
601
            return $this->_cacheById[$id];
602
603
        }
604
605
        return null;
606
607
    }
608
609
    /**
610
     * Identify whether in cache by ID
611
     *
612
     * @param $id
613
     * @return bool
614
     */
615
    protected function isCachedById(int $id)
616
    {
617
        return array_key_exists($id, $this->_cacheById);
618
    }
619
620
    /**
621
     * @param BaseModel $model
622
     * @return $this
623
     */
624
    protected function cacheById(BaseModel $model)
625
    {
626
627
        // Check if already in cache
628
        if (!$id = $this->isCachedById($model->id)) {
629
630
            // Cache it
631
            $this->_cacheById[$id] = $model;
632
633
        }
634
635
        return $this;
636
637
    }
638
639
    /**
640
     * @param Record $record
641
     * @return BaseModel|null
642
     */
643
    public function findCacheByRecord(Record $record)
644
    {
645
646
        // Check if already in addToCache by id
647
        if ($id = $this->isCachedById($record->id)) {
648
649
            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...
650
651
        }
652
653
        return null;
654
    }
655
656
    /**
657
     * @param BaseModel $model
658
     * @return static
659
     */
660
    public function addToCache(BaseModel $model)
661
    {
662
663
        $this->cacheById($model);
664
665
        return $this;
666
    }
667
668
669
    /*******************************************
670
     * FIND/GET RECORD BY ID
671
     *******************************************/
672
673
    /**
674
     * @param int $id
675
     * @param string $toScenario
676
     * @return Record|null
677
     */
678
    public function findRecordById(int $id, string $toScenario = null)
679
    {
680
681
        return $this->findRecordByCondition(
682
            ['id' => $id],
683
            $toScenario
684
        );
685
686
    }
687
688
    /**
689
     * @param int $id
690
     * @param string|null $toScenario
691
     * @return Record
692
     */
693
    public function getRecordById(int $id, string $toScenario = null): Record
694
    {
695
696
        return $this->getRecordByCondition(
697
            ['id' => $id],
698
            $toScenario
699
        );
700
701
    }
702
703
704
    /*******************************************
705
     * EXCEPTIONS
706
     *******************************************/
707
708
    /**
709
     * @throws ModelNotFoundException
710
     */
711
    protected function notFoundException()
712
    {
713
714
        throw new ModelNotFoundException(
715
            sprintf(
716
                "Model does not exist."
717
            )
718
        );
719
720
    }
721
722
    /**
723
     * @param int|null $id
724
     * @throws ModelNotFoundException
725
     */
726
    protected function notFoundByIdException(int $id = null)
727
    {
728
729
        throw new ModelNotFoundException(
730
            sprintf(
731
                'Model does not exist with the id "%s".',
732
                (string)$id
733
            )
734
        );
735
736
    }
737
738
    /**
739
     * @param null $criteria
740
     * @throws ModelNotFoundException
741
     */
742
    protected function notFoundByCriteriaException($criteria = null)
743
    {
744
745
        throw new ModelNotFoundException(
746
            sprintf(
747
                'Model does not exist with the criteria "%s".',
748
                (string)JsonHelper::encode($criteria)
749
            )
750
        );
751
752
    }
753
754
    /**
755
     * @param null $condition
756
     * @throws ModelNotFoundException
757
     */
758
    protected function notFoundByConditionException($condition = null)
759
    {
760
761
        throw new ModelNotFoundException(
762
            sprintf(
763
                'Model does not exist with the condition "%s".',
764
                (string)JsonHelper::encode($condition)
765
            )
766
        );
767
768
    }
769
770
}
771