Completed
Push — master ( 9817c8...2b2ac0 )
by Oscar
05:39
created

RowCollection::offsetUnset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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();
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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, $row->getTable(), $row, $relation[1]);
0 ignored issues
show
Bug introduced by
The variable $row seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
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(array $bannedEntities = array())
255
    {
256
        $table = $this->getTable();
257
258
        if (!empty($bannedEntities) && in_array($table->name, $bannedEntities)) {
259
            return;
260
        }
261
262
        $rows = [];
263
264
        foreach ($this->rows as $row) {
265
            $rows[] = $row->toArray($bannedEntities);
266
        }
267
268
        return $rows;
269
    }
270
271
    /**
272
     * Filter the rows by a value.
273
     *
274
     * @param callable $filter
275
     *
276
     * @return RowCollection
277
     */
278
    public function filter(callable $filter)
279
    {
280
        return $this->table->createCollection(array_filter($this->rows, $filter));
281
    }
282
283
    /**
284
     * Find a row by a value.
285
     *
286
     * @param callable $filter
287
     *
288
     * @return Row|null The rows found
289
     */
290
    public function find(callable $filter)
291
    {
292
        foreach ($this->rows as $row) {
293
            if ($filter($row) === true) {
294
                return $row;
295
            }
296
        }
297
    }
298
299
    /**
300
     * Join two related tables.
301
     * 
302
     * @param Table               $table1
303
     * @param RowCollection|array $rows1
304
     * @param Table               $table2
305
     * @param RowCollection|array $rows2
306
     * @param array               $relation
307
     */
308
    private static function join(Table $table1, $rows1, Table $table2, $rows2, array $relation)
309
    {
310
        if ($relation[0] === Scheme::HAS_ONE) {
311
            list($table2, $rows2, $table1, $rows1) = [$table1, $rows1, $table2, $rows2];
312
        }
313
314
        foreach ($rows1 as $row) {
315
            $row->{$table2->name} = $table2->createCollection();
316
        }
317
318
        foreach ($rows2 as $row) {
319
            $id = $row->{$relation[1]};
320
321
            if (isset($rows1[$id])) {
322
                $rows1[$id]->{$table2->name}[] = $row;
323
                $row->{$table1->name} = $rows1[$id];
324
            } else {
325
                $row->{$table1->name} = null;
326
            }
327
        }
328
    }
329
}
330