Completed
Push — master ( 0eeb04...4002ae )
by Oscar
02:31
created

RowCollection::__get()   C

Complexity

Conditions 10
Paths 10

Size

Total Lines 59
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 15
Bugs 2 Features 1
Metric Value
c 15
b 2
f 1
dl 0
loc 59
rs 6.5919
cc 10
eloc 31
nc 10
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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