Passed
Pull Request — master (#241)
by Arman
03:23
created

ModelCollection   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 168
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 45
dl 0
loc 168
rs 10
c 1
b 0
f 0
wmc 20

11 Methods

Rating   Name   Duplication   Size   Complexity  
A first() 0 7 2
A __construct() 0 6 2
A last() 0 4 2
A validateModel() 0 4 2
A count() 0 4 1
A processModels() 0 14 3
A remove() 0 11 1
A getIterator() 0 11 3
A add() 0 13 2
A all() 0 4 1
A isEmpty() 0 3 1
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.6
13
 */
14
15
namespace Quantum\Model;
16
17
use InvalidArgumentException;
18
use IteratorAggregate;
19
use Countable;
20
use Generator;
21
22
/**
23
 * Class ModelCollection
24
 * @package Quantum\Model
25
 */
26
class ModelCollection implements Countable, IteratorAggregate
27
{
28
29
    /**
30
     * @var QtModel[]
31
     */
32
    private $models = [];
33
34
    /**
35
     * @var iterable
36
     */
37
    private $originalModels;
38
39
    /**
40
     * @var bool
41
     */
42
    private $modelsProcessed = false;
43
44
    /**
45
     * @param iterable $models
46
     */
47
    public function __construct(iterable $models = [])
48
    {
49
        $this->originalModels = $models;
50
51
        if (is_array($models)) {
52
            $this->processModels();
53
        }
54
    }
55
56
    /**
57
     * Add a model to the collection
58
     * @param QtModel $model
59
     * @return self
60
     */
61
    public function add(QtModel $model): self
62
    {
63
        $this->processModels();
64
65
        $this->models[] = $model;
66
67
        if (!is_array($this->originalModels)) {
68
            $this->originalModels = $this->models;
69
        } else {
70
            $this->originalModels[] = $model;
71
        }
72
73
        return $this;
74
    }
75
76
    /**
77
     * Remove a model from the collection
78
     * @param QtModel $model
79
     * @return self
80
     */
81
    public function remove(QtModel $model): self
82
    {
83
        $this->processModels();
84
85
        $this->models = array_filter($this->models, function ($m) use ($model) {
86
            return $m !== $model;
87
        });
88
89
        $this->originalModels = $this->models;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Get all models as an array
96
     * @return QtModel[]
97
     */
98
    public function all(): array
99
    {
100
        $this->processModels();
101
        return $this->models;
102
    }
103
104
    /**
105
     * Get the count of models in the collection
106
     * @return int
107
     */
108
    public function count(): int
109
    {
110
        $this->processModels();
111
        return count($this->models);
112
    }
113
114
    /**
115
     * Get the first model in the collection
116
     * @return QtModel|null
117
     */
118
    public function first(): ?QtModel
119
    {
120
        foreach ($this->getIterator() as $model) {
121
            return $model;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $model could return the type Quantum\Model\QtModel[] which is incompatible with the type-hinted return Quantum\Model\QtModel|null. Consider adding an additional type-check to rule them out.
Loading history...
122
        }
123
124
        return null;
125
    }
126
127
    /**
128
     * Get the last model in the collection
129
     * @return QtModel|null
130
     */
131
    public function last(): ?QtModel
132
    {
133
        $this->processModels();
134
        return empty($this->models) ? null : end($this->models);
135
    }
136
137
    /**
138
     * Check if the collection is empty
139
     * @return bool
140
     */
141
    public function isEmpty(): bool
142
    {
143
        return $this->first() === null;
144
    }
145
146
    /**
147
     * Get an iterator for the collection
148
     * @return Generator
149
     * @throws InvalidArgumentException
150
     */
151
    public function getIterator(): Generator
152
    {
153
        if ($this->modelsProcessed) {
154
            yield from $this->models;
155
        } else {
156
            foreach ($this->originalModels as $model) {
157
                $this->validateModel($model);
158
                yield $model;
159
            }
160
161
            $this->processModels();
162
        }
163
    }
164
165
    /**
166
     * Process models from original source into the internal array
167
     * @throws InvalidArgumentException
168
     */
169
    private function processModels()
170
    {
171
        if ($this->modelsProcessed) {
172
            return;
173
        }
174
175
        $this->models = [];
176
177
        foreach ($this->originalModels as $model) {
178
            $this->validateModel($model);
179
            $this->models[] = $model;
180
        }
181
182
        $this->modelsProcessed = true;
183
    }
184
185
    /**
186
     * Validate that an item is a QtModel instance
187
     * @param mixed $model
188
     * @throws InvalidArgumentException
189
     */
190
    private function validateModel($model): void
191
    {
192
        if (!$model instanceof QtModel) {
193
            throw new InvalidArgumentException('All items must be instances of QtModel.');
194
        }
195
    }
196
}