Completed
Push — master ( e4d56c...8e58c0 )
by Ivan
02:47
created

TableQueryIterator   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 78.95%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 2
dl 0
loc 224
ccs 105
cts 133
cp 0.7895
rs 4.5599
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A key() 0 4 1
D current() 0 82 27
A values() 0 13 6
A rewind() 0 6 1
B next() 0 36 9
A valid() 0 4 1
A offsetGet() 0 16 5
A offsetExists() 0 16 5
A offsetSet() 0 4 1
A offsetUnset() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like TableQueryIterator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TableQueryIterator, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace vakata\database\schema;
3
4
use vakata\collection\Collection;
5
use vakata\database\DBException;
6
7
/**
8
 * A table query iterator
9
 */
10
class TableQueryIterator implements \Iterator, \ArrayAccess
11
{
12
    const SEP = '___';
13
    /**
14
     * @var array
15
     */
16
    protected $pkey;
17
    /**
18
     * @var Collection
19
     */
20
    protected $result;
21
    /**
22
     * @var array[]
23
     */
24
    protected $relations;
25
    /**
26
     * @var array[]
27
     */
28
    protected $aliases;
29
    /**
30
     * @var string|null
31
     */
32
    protected $primary = null;
33
    /**
34
     * @var int
35
     */
36
    protected $fetched = 0;
37
38 90
    public function __construct(Collection $result, array $pkey, array $relations = [], array $aliases = [])
39
    {
40 90
        $this->pkey = $pkey;
41 90
        $this->result = $result;
42 90
        $this->relations = $relations;
43 90
        $this->aliases = $aliases;
44 90
    }
45
46 90
    public function key()
47
    {
48 90
        return $this->fetched;
49
    }
50 90
    public function current()
51
    {
52 90
        $result = null;
53 90
        $remove = [];
54 90
        while ($this->result->valid()) {
55 90
            $row = $this->result->current();
56 90
            $pk = [];
57 90
            foreach ($this->pkey as $field) {
58 90
                $pk[$field] = $row[$field];
59
            }
60 90
            $pk = json_encode($pk);
61 90
            if ($pk === false) {
62
                throw new DBException('Invalid PK');
63
            }
64 90
            if ($this->primary !== null && $pk !== $this->primary) {
65 66
                break;
66
            }
67 90
            $this->primary = $pk;
68 90
            if (!$result) {
69 90
                $result = $row;
70
            }
71 90
            foreach ($this->relations as $name => $relation) {
72 18
                $relation = $relation[0];
73 18
                $fields = [];
74 18
                $exists = false;
75 18
                foreach ($relation->table->getColumns() as $column) {
76 18
                    $nm = $name . static::SEP . $column;
77 18
                    if (isset($this->aliases[$nm])) {
78 18
                        $nm = $this->aliases[$nm];
79
                    }
80 18
                    $fields[$column] = $row[$nm];
81 18
                    if (!$exists && $row[$nm] !== null) {
82 18
                        $exists = true;
83
                    }
84 18
                    $remove[] = $nm; // $name . static::SEP . $column;
85
                }
86 18
                $temp  = &$result;
87 18
                $parts = explode(static::SEP, $name);
88 18
                $name  = array_pop($parts);
89 18
                if (!$exists && !count($parts) && !isset($temp[$name])) {
90 6
                    $temp[$name] = $relation->many ? [ '___clean' => true ] : null;
91
                }
92 18
                if ($exists) {
93 18
                    $full  = '';
94 18
                    foreach ($parts as $item) {
95 3
                        $full = $full ? $full . static::SEP . $item : $item;
96 3
                        $temp = &$temp[$item];
97 3
                        $rpk = [];
98 3
                        foreach ($this->relations[$full][0]->table->getPrimaryKey() as $pkey) {
99 3
                            $nm = $full . static::SEP . $pkey;
100 3
                            if (isset($this->aliases[$nm])) {
101 3
                                $nm = $this->aliases[$nm];
102
                            }
103 3
                            $rpk[$pkey] = $row[$nm];
104
                        }
105 3
                        $temp = &$temp[json_encode($rpk)];
106
                    }
107 18
                    if (!isset($temp[$name])) {
108 18
                        $temp[$name] = $relation->many ? [ '___clean' => true ] : null;
109
                    }
110 18
                    $temp = &$temp[$name];
111 18
                    if ($relation->many) {
112 12
                        $rpk = [];
113 12
                        foreach ($relation->table->getPrimaryKey() as $field) {
114 12
                            $rpk[$field] = $fields[$field];
115
                        }
116 12
                        $temp[json_encode($rpk)] = array_merge($temp[json_encode($rpk)] ?? [], $fields);
117
                    } else {
118 12
                        $temp = array_merge($temp ?? [], $fields);
119
                    }
120
                }
121
            }
122 90
            $this->result->next();
123
        }
124 90
        if ($result) {
125 90
            foreach ($remove as $name) {
126 18
                unset($result[$name]);
127
            }
128 90
            $result = $this->values($result);
129
        }
130 90
        return $result;
131
    }
132 90
    protected function values(array $data)
133
    {
134 90
        foreach ($data as $k => $v) {
135 90
            if (is_array($v) && isset($v['___clean']) && $v['___clean'] === true) {
136 12
                unset($v['___clean']);
137 12
                $data[$k] = array_values($v);
138 12
                foreach ($data[$k] as $kk => $vv) {
139 12
                    $data[$k][$kk] = $this->values($vv);
140
                }
141
            }
142
        }
143 90
        return $data;
144
    }
145
146 90
    public function rewind()
147
    {
148 90
        $this->fetched = 0;
149 90
        $this->primary = null;
150 90
        return $this->result->rewind();
151
    }
152 90
    public function next()
153
    {
154 90
        if ($this->primary === null) {
155
            $this->result->next();
156
            if ($this->result->valid()) {
157
                $row = $this->result->current();
158
                $temp = [];
159
                foreach ($this->pkey as $field) {
160
                    $temp[$field] = $row[$field];
161
                }
162
                $pk = json_encode($temp);
163
                if ($pk === false) {
164
                    throw new DBException('Invalid PK');
165
                }
166
                $this->primary = $pk;
167
                return;
168
            }
169
        }
170 90
        $this->fetched ++;
171 90
        while ($this->result->valid()) {
172 66
            $row = $this->result->current();
173 66
            $pk = [];
174 66
            foreach ($this->pkey as $field) {
175 66
                $pk[$field] = $row[$field];
176
            }
177 66
            $pk = json_encode($pk);
178 66
            if ($pk === false) {
179
                throw new DBException('Invalid PK');
180
            }
181 66
            if ($this->primary !== $pk) {
182 66
                $this->primary = $pk;
183 66
                break;
184
            }
185
            $this->result->next();
186
        }
187 90
    }
188 90
    public function valid()
189
    {
190 90
        return $this->result->valid();
191
    }
192
193 30
    public function offsetGet($offset)
194
    {
195 30
        $index = $this->fetched;
196 30
        $item = null;
197 30
        foreach ($this as $k => $v) {
198 30
            if ($k === $offset) {
199 30
                $item = $v;
200
            }
201
        }
202 30
        foreach ($this as $k => $v) {
203 30
            if ($k === $index) {
204 30
                break;
205
            }
206
        }
207 30
        return $item;
208
    }
209
    public function offsetExists($offset)
210
    {
211
        $index = $this->fetched;
212
        $exists = false;
213
        foreach ($this as $k => $v) {
214
            if ($k === $offset) {
215
                $exists = true;
216
            }
217
        }
218
        foreach ($this as $k => $v) {
219
            if ($k === $index) {
220
                break;
221
            }
222
        }
223
        return $exists;
224
    }
225
    public function offsetSet($offset, $value)
226
    {
227
        throw new DBException('Invalid call to offsetSet');
228
    }
229
    public function offsetUnset($offset)
230
    {
231
        throw new DBException('Invalid call to offsetUnset');
232
    }
233
}
234