Passed
Push — master ( a018a6...5a9c7e )
by Nate
02:50
created

Object::createFromRecord()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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