Passed
Pull Request — 2.x (#53)
by Aleksei
19:04 queued 01:32
created

State   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 81
dl 0
loc 290
ccs 108
cts 108
cp 1
rs 8.48
c 2
b 0
f 0
wmc 49

24 Methods

Rating   Name   Duplication   Size   Complexity  
A getPrimaryKeys() 0 14 5
A registerForeignKey() 0 3 1
A getForeignKeys() 0 3 1
A getName() 0 3 1
A getColumns() 0 3 1
A setPrimaryKeys() 0 3 1
A setName() 0 3 1
A __construct() 0 3 1
A __clone() 0 12 4
A hasForeignKey() 0 3 1
A registerColumn() 0 3 1
A forgetColumn() 0 11 3
A registerIndex() 0 3 1
A getIndexes() 0 3 1
A hasColumn() 0 3 1
A forgetIndex() 0 7 3
A hasIndex() 0 3 1
A findIndex() 0 9 3
A findForeignKey() 0 9 3
A remountElements() 0 20 4
A forgetForeignKey() 0 7 3
A findColumn() 0 9 3
A syncState() 0 23 4
A forgerForeignKey() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like State 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.

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 State, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Schema;
13
14
/**
15
 * TableSchema helper used to store original table elements and run comparation between them.
16
 *
17
 * Attention: this state IS MUTABLE!
18
 */
19
final class State
20
{
21
    /** @var AbstractColumn[] */
22
    private array $columns = [];
23
24
    /** @var AbstractIndex[] */
25
    private array $indexes = [];
26
27
    /** @var AbstractForeignKey[] */
28
    private array $foreignKeys = [];
29
30
    /**
31
     * Primary key columns are stored separately from other indexes and
32
     * can only be modified during table creation.
33
     */
34
    private array $primaryKeys = [];
35
36
    /**
37
     * @psalm-param non-empty-string $name
38
     */
39 1974
    public function __construct(
40
        private string $name
41
    ) {
42 1974
    }
43
44
    /**
45
     * Cloning all elements.
46
     */
47 1950
    public function __clone()
48
    {
49 1950
        foreach ($this->columns as $name => $column) {
50 1950
            $this->columns[$name] = clone $column;
51
        }
52
53 1950
        foreach ($this->indexes as $name => $index) {
54 458
            $this->indexes[$name] = clone $index;
55
        }
56
57 1950
        foreach ($this->foreignKeys as $name => $foreignKey) {
58 224
            $this->foreignKeys[$name] = clone $foreignKey;
59
        }
60 1950
    }
61
62
    /**
63
     * Set table name. Operation will be applied at moment of saving.
64
     *
65
     * @psalm-param non-empty-string $name
66
     */
67 1974
    public function setName(string $name): void
68
    {
69 1974
        $this->name = $name;
70 1974
    }
71
72
    /**
73
     * @psalm-return non-empty-string
74
     */
75 1974
    public function getName(): string
76
    {
77 1974
        return $this->name;
78
    }
79
80
    /**
81
     * @return AbstractColumn[]
82
     */
83 1958
    public function getColumns(): array
84
    {
85 1958
        return $this->columns;
86
    }
87
88
    /**
89
     * @return AbstractIndex[]
90
     */
91 1952
    public function getIndexes(): array
92
    {
93 1952
        return $this->indexes;
94
    }
95
96
    /**
97
     * @return AbstractForeignKey[]
98
     */
99 1954
    public function getForeignKeys(): array
100
    {
101 1954
        return $this->foreignKeys;
102
    }
103
104 1928
    public function setPrimaryKeys(array $columns): void
105
    {
106 1928
        $this->primaryKeys = $columns;
107 1928
    }
108
109
    /**
110
     * Method combines primary keys with primary keys automatically calculated based on registered columns.
111
     */
112 1954
    public function getPrimaryKeys(): array
113
    {
114 1954
        $primaryColumns = [];
115 1954
        foreach ($this->getColumns() as $column) {
116 1946
            $type = $column->getAbstractType();
117 1946
            if ($type === 'primary' || $type === 'bigPrimary') {
118 1366
                if (!\in_array($column->getName(), $this->primaryKeys, true)) {
119
                    //Only columns not listed as primary keys already
120 1364
                    $primaryColumns[] = $column->getName();
121
                }
122
            }
123
        }
124
125 1954
        return array_unique(array_merge($this->primaryKeys, $primaryColumns));
126
    }
127
128
    /**
129
     * @psalm-param non-empty-string $name
130
     */
131 1950
    public function hasColumn(string $name): bool
132
    {
133 1950
        return $this->findColumn($name) !== null;
134
    }
135
136 458
    public function hasIndex(array $columns = []): bool
137
    {
138 458
        return $this->findIndex($columns) !== null;
139
    }
140
141 224
    public function hasForeignKey(array $columns): bool
142
    {
143 224
        return $this->findForeignKey($columns) !== null;
144
    }
145
146 1950
    public function registerColumn(AbstractColumn $column): void
0 ignored issues
show
Bug introduced by
The type Cycle\Database\Schema\AbstractColumn was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
147
    {
148 1950
        $this->columns[$column->getName()] = $column;
149 1950
    }
150
151 458
    public function registerIndex(AbstractIndex $index): void
152
    {
153 458
        $this->indexes[$index->getName()] = $index;
154 458
    }
155
156 224
    public function registerForeignKey(AbstractForeignKey $reference): void
157
    {
158 224
        $this->foreignKeys[$reference->getName()] = $reference;
159 224
    }
160
161
    /**
162
     * Drop column from table schema.
163
     */
164 32
    public function forgetColumn(AbstractColumn $column): self
165
    {
166 32
        foreach ($this->columns as $name => $columnSchema) {
167
            // todo: need better compare
168 32
            if ($columnSchema == $column) {
169 32
                unset($this->columns[$name]);
170 32
                break;
171
            }
172
        }
173
174 32
        return $this;
175
    }
176
177
    /**
178
     * Drop index from table schema using it's name or forming columns.
179
     */
180 102
    public function forgetIndex(AbstractIndex $index): void
181
    {
182 102
        foreach ($this->indexes as $name => $indexSchema) {
183
            // todo: need better compare
184 102
            if ($indexSchema == $index) {
185 102
                unset($this->indexes[$name]);
186 102
                break;
187
            }
188
        }
189 102
    }
190
191
    /**
192
     * Drop foreign key from table schema using it's forming column.
193
     * @deprecated Since cycle/database 2.2.0, use {@see forgetForeignKey()} instead.
194 224
     */
195
    public function forgerForeignKey(AbstractForeignKey $foreignKey): void
196 224
    {
197
        $this->forgetForeignKey($foreignKey);
198 224
    }
199 224
200 224
    /**
201
     * Drop foreign key from table schema using it's forming column.
202
     * @since 2.2.0
203 224
     */
204
    public function forgetForeignKey(AbstractForeignKey $foreignKey): void
205
    {
206
        foreach ($this->foreignKeys as $name => $foreignSchema) {
207
            // todo: need better compare
208 1950
            if ($foreignSchema == $foreignKey) {
209
                unset($this->foreignKeys[$name]);
210 1950
                break;
211 1926
            }
212 1682
        }
213
    }
214
215
    /**
216 1948
     * @psalm-param non-empty-string $name
217
     */
218
    public function findColumn(string $name): ?AbstractColumn
219
    {
220
        foreach ($this->columns as $column) {
221
            if ($column->getName() === $name) {
222 458
                return $column;
223
            }
224 458
        }
225 458
226 458
        return null;
227
    }
228
229
    /**
230 458
     * Find index by it's columns or return null.
231
     */
232
    public function findIndex(array $columns): ?AbstractIndex
233
    {
234
        foreach ($this->indexes as $index) {
235
            if ($index->getColumnsWithSort() === $columns) {
236 224
                return $index;
237
            }
238 224
        }
239 224
240 224
        return null;
241
    }
242
243
    /**
244 224
     * Find foreign key by it's column or return null.
245
     */
246
    public function findForeignKey(array $columns): ?AbstractForeignKey
247
    {
248
        foreach ($this->foreignKeys as $fk) {
249
            if ($fk->getColumns() === $columns) {
250 1974
                return $fk;
251
            }
252 1974
        }
253 1974
254 1944
        return null;
255
    }
256
257 1974
    /**
258 1974
     * Remount elements under their current name.
259 458
     */
260
    public function remountElements(): void
261
    {
262 1974
        $columns = [];
263 1974
        foreach ($this->columns as $column) {
264 224
            $columns[$column->getName()] = $column;
265
        }
266
267 1974
        $indexes = [];
268 1974
        foreach ($this->indexes as $index) {
269 1974
            $indexes[$index->getName()] = $index;
270 1974
        }
271
272
        $foreignKeys = [];
273
        foreach ($this->foreignKeys as $fk) {
274
            $foreignKeys[$fk->getName()] = $fk;
275
        }
276 1974
277
        $this->columns = $columns;
278 1974
        $this->indexes = $indexes;
279 1974
        $this->foreignKeys = $foreignKeys;
280
    }
281 1974
282 1974
    /**
283 1944
     * Re-populate schema elements using other state as source. Elements will be cloned under their
284
     * schema name.
285
     */
286 1974
    public function syncState(self $source): self
287 1974
    {
288 458
        $this->name = $source->name;
289
        $this->primaryKeys = $source->primaryKeys;
290
291 1974
        $this->columns = [];
292 1974
        foreach ($source->columns as $name => $column) {
293 224
            $this->columns[$name] = clone $column;
294
        }
295
296 1974
        $this->indexes = [];
297
        foreach ($source->indexes as $name => $index) {
298 1974
            $this->indexes[$name] = clone $index;
299
        }
300
301
        $this->foreignKeys = [];
302
        foreach ($source->foreignKeys as $name => $foreignKey) {
303
            $this->foreignKeys[$name] = clone $foreignKey;
304
        }
305
306
        $this->remountElements();
307
308
        return $this;
309
    }
310
}
311