Completed
Push — master ( 663ec8...a39ee3 )
by Oscar
02:47
created

RowCollection::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace SimpleCrud;
4
5
use SimpleCrud\Scheme\Scheme;
6
use ArrayAccess;
7
use Iterator;
8
use Countable;
9
10
/**
11
 * Stores a collection of rows.
12
 */
13
class RowCollection extends AbstractRow implements ArrayAccess, Iterator, Countable
14
{
15
    private $rows = [];
16
    private $loadedRelations = [];
17
18
    /**
19
     * Debug info.
20
     * 
21
     * @return array
22
     */
23
    public function __debugInfo()
24
    {
25
        return [
26
            'table' => $this->getTable()->name,
27
            'rows' => $this->rows,
28
        ];
29
    }
30
31
    /**
32
     * Magic method to get properties from all rows.
33
     *
34
     * @see self::get()
35
     */
36
    public function __get($name)
37
    {
38
        $table = $this->getTable();
39
        $scheme = $table->getScheme();
40
41
        //It's a field
42
        if (isset($scheme['fields'][$name])) {
43
            $result = [];
44
45
            foreach ($this->rows as $id => $row) {
46
                $result[$id] = $row->$name;
47
            }
48
49
            return $result;
50
        }
51
52
        if (!isset($scheme['relations'][$name])) {
53
            throw new SimpleCrudException(sprintf('Undefined property "%s"', $name));
54
        }
55
56
        $relation = $scheme['relations'][$name];
57
        $related = $this->getDatabase()->$name;
58
        $result = $related->createCollection();
59
60
        //It's already loaded relation
61
        if (in_array($name, $this->loadedRelations, true)) {
62
            if ($relation[0] === Scheme::HAS_ONE) {
63
                foreach ($this->rows as $row) {
64
                    $result[] = $row->$name;
65
                }
66
67
                return $result;
68
            }
69
70
            foreach ($this->rows as $row) {
71
                foreach ($row->$name as $r) {
72
                    $result[] = $r;
73
                }
74
            }
75
76
            return $result;
77
        }
78
79
        //Load the relation
80
        $select = $related->select()->relatedWith($this);
81
82
        //Many to many
83
        if ($relation[0] === Scheme::HAS_MANY_TO_MANY) {
84
            $statement = $select();
85
86
            foreach ($this->rows as $row) {
87
                $row->{$related->name} = $related->createCollection();
88
            }
89
90
            while (($data = $statement->fetch())) {
91
                $this->rows[$data[$relation[2]]]->{$related->name}[] = $result[] = $select->createRow($data);
92
            }
93
94
            return $result;
95
        }
96
97
        $rows = $select->all(true)->run();
98
99
        //Join the relations and rows
100
        self::join($table, $this->rows, $related, $rows, $relation);
101
        $this->loadedRelations[] = $name;
102
103
        foreach ($rows as $row) {
104
            $result[] = $row;
105
        }
106
107
        return $result;
108
    }
109
110
    /**
111
     * Magic method to set properties to all rows.
112
     *
113
     * @param string $name
114
     * @param mixed  $value
115
     */
116
    public function __set($name, $value)
117
    {
118
        $table = $this->getTable();
119
        $scheme = $table->getScheme();
120
121
        //It's a field
122
        if (isset($scheme['fields'][$name])) {
123
            foreach ($this->rows as $row) {
124
                $row->$name = $value;
125
            }
126
127
            return;
128
        }
129
130
        //It's a relation
131
        if (!isset($scheme['relations'][$name])) {
132
            throw new SimpleCrudException(sprintf('Undefined property "%s"'), $name);
133
        }
134
135
        $relation = $scheme['relations'][$name];
136
137
        //Check types
138
        if ($value === null) {
139
            $value = $table->createCollection();
140
        } elseif (!($value instanceof self)) {
141
            throw new SimpleCrudException(sprintf('Invalid value: %s must be a RowCollection instance or null', $name));
142
        }
143
144
        //Join the relations and rows
145
        self::join($table, $this->rows, $value->getTable(), $value, $relation);
146
        $this->loadedRelations[] = $name;
147
    }
148
149
    /**
150
     * Magic method to check if a property is defined or not.
151
     *
152
     * @param string $name Property name
153
     *
154
     * @return bool
155
     */
156
    public function __isset($name)
157
    {
158
        $scheme = $this->getTable()->getScheme();
159
160
        return isset($scheme['fields'][$name]) || isset($this->loadedRelations[$name]);
161
    }
162
163
    /**
164
     * @see ArrayAccess
165
     */
166
    public function offsetSet($offset, $value)
167
    {
168
        if (!($value instanceof Row)) {
169
            throw new SimpleCrudException('Only instances of SimpleCrud\\Row must be added to collections');
170
        }
171
172
        if (empty($value->id)) {
173
            throw new SimpleCrudException('Only rows with the defined id must be added to collections');
174
        }
175
176
        $this->rows[$value->id] = $value;
177
    }
178
179
    /**
180
     * @see ArrayAccess
181
     */
182
    public function offsetExists($offset)
183
    {
184
        return isset($this->rows[$offset]);
185
    }
186
187
    /**
188
     * @see ArrayAccess
189
     */
190
    public function offsetUnset($offset)
191
    {
192
        unset($this->rows[$offset]);
193
    }
194
195
    /**
196
     * @see ArrayAccess
197
     */
198
    public function offsetGet($offset)
199
    {
200
        return isset($this->rows[$offset]) ? $this->rows[$offset] : null;
201
    }
202
203
    /**
204
     * @see Iterator
205
     */
206
    public function rewind()
207
    {
208
        return reset($this->rows);
209
    }
210
211
    /**
212
     * @see Iterator
213
     */
214
    public function current()
215
    {
216
        return current($this->rows);
217
    }
218
219
    /**
220
     * @see Iterator
221
     */
222
    public function key()
223
    {
224
        return key($this->rows);
225
    }
226
227
    /**
228
     * @see Iterator
229
     */
230
    public function next()
231
    {
232
        return next($this->rows);
233
    }
234
235
    /**
236
     * @see Iterator
237
     */
238
    public function valid()
239
    {
240
        return key($this->rows) !== null;
241
    }
242
243
    /**
244
     * @see Countable
245
     */
246
    public function count()
247
    {
248
        return count($this->rows);
249
    }
250
251
    /**
252
     * {@inheritdoc}
253
     */
254
    public function toArray($recursive = true, array $bannedEntities = [])
255
    {
256
        if (!$recursive) {
257
            return $this->rows;
258
        }
259
260
        $table = $this->getTable();
261
262
        if (!empty($bannedEntities) && in_array($table->name, $bannedEntities)) {
263
            return;
264
        }
265
266
        $rows = [];
267
268
        foreach ($this->rows as $row) {
269
            $rows[] = $row->toArray($recursive, $bannedEntities);
270
        }
271
272
        return $rows;
273
    }
274
275
    /**
276
     * Filter the rows by a value.
277
     *
278
     * @param callable $filter
279
     *
280
     * @return RowCollection
281
     */
282
    public function filter(callable $filter)
283
    {
284
        return $this->table->createCollection(array_filter($this->rows, $filter));
285
    }
286
287
    /**
288
     * Find a row by a value.
289
     *
290
     * @param callable $filter
291
     *
292
     * @return Row|null The rows found
293
     */
294
    public function find(callable $filter)
295
    {
296
        foreach ($this->rows as $row) {
297
            if ($filter($row) === true) {
298
                return $row;
299
            }
300
        }
301
    }
302
303
    /**
304
     * Join two related tables.
305
     * 
306
     * @param Table               $table1
307
     * @param RowCollection|array $rows1
308
     * @param Table               $table2
309
     * @param RowCollection|array $rows2
310
     * @param array               $relation
311
     */
312
    private static function join(Table $table1, $rows1, Table $table2, $rows2, array $relation)
313
    {
314
        if ($relation[0] === Scheme::HAS_ONE) {
315
            list($table2, $rows2, $table1, $rows1) = [$table1, $rows1, $table2, $rows2];
316
        }
317
318
        foreach ($rows1 as $row) {
319
            $row->{$table2->name} = $table2->createCollection();
320
        }
321
322
        foreach ($rows2 as $row) {
323
            $id = $row->{$relation[1]};
324
325
            if (isset($rows1[$id])) {
326
                $rows1[$id]->{$table2->name}[] = $row;
327
                $row->{$table1->name} = $rows1[$id];
328
            } else {
329
                $row->{$table1->name} = null;
330
            }
331
        }
332
    }
333
}
334