Model   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 370
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 18
Bugs 3 Features 4
Metric Value
wmc 47
c 18
b 3
f 4
lcom 2
cbo 6
dl 0
loc 370
rs 8.439

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A assignFields() 0 11 3
B assignIndexes() 0 20 6
A assignRelations() 0 14 4
A table() 0 4 1
A entity() 0 4 1
A alias() 0 8 2
A hasField() 0 4 1
A fields() 0 4 1
A assertField() 0 6 2
A field() 0 6 1
A primaryFields() 0 15 4
A indexFields() 0 14 3
A hasIndex() 0 4 1
A indexes() 0 4 1
A index() 0 8 2
A referredIn() 0 13 3
A hasRelations() 0 4 1
A hasRelation() 0 4 1
A relations() 0 4 1
A relation() 0 8 2
A findRelationByName() 0 10 4

How to fix   Complexity   

Complex Class

Complex classes like Model 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 Model, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Storage package
5
 *
6
 * (c) Michal Wachowski <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Moss\Storage\Model;
13
14
use Moss\Storage\GetTypeTrait;
15
use Moss\Storage\Model\Definition\FieldInterface;
16
use Moss\Storage\Model\Definition\IndexInterface;
17
use Moss\Storage\Model\Definition\RelationInterface;
18
use Moss\Storage\NormalizeNamespaceTrait;
19
20
/**
21
 * Model describing entity and its relationship to other entities
22
 *
23
 * @author  Michal Wachowski <[email protected]>
24
 * @package Moss\Storage
25
 */
26
class Model implements ModelInterface
27
{
28
    use NormalizeNamespaceTrait;
29
    use GetTypeTrait;
30
31
    protected $table;
32
    protected $entity;
33
    protected $alias;
34
35
    /**
36
     * @var FieldInterface[]
37
     */
38
    protected $fields = [];
39
40
    /**
41
     * @var IndexInterface[]
42
     */
43
    protected $indexes = [];
44
45
    /**
46
     * @var RelationInterface[]
47
     */
48
    protected $relations = [];
49
50
    /**
51
     * Constructor
52
     *
53
     * @param string                    $entityClass
54
     * @param string                    $table
55
     * @param FieldInterface[]    $fields
56
     * @param IndexInterface[]    $indexes
57
     * @param RelationInterface[] $relations
58
     *
59
     * @throws ModelException
60
     */
61
    public function __construct($entityClass, $table, array $fields, array $indexes = [], array $relations = [])
62
    {
63
        $this->table = $table;
64
        $this->entity = $entityClass ? $this->normalizeNamespace($entityClass) : null;
65
66
        $this->assignFields($fields);
67
        $this->assignIndexes($indexes);
68
        $this->assignRelations($relations);
69
    }
70
71
    /**
72
     * Assigns fields to model
73
     *
74
     * @param array $fields
75
     *
76
     * @throws ModelException
77
     */
78
    protected function assignFields($fields)
79
    {
80
        foreach ($fields as $field) {
81
            if (!$field instanceof FieldInterface) {
82
                throw new ModelException(sprintf('Field must be an instance of FieldInterface, got "%s"', $this->getType($field)));
83
            }
84
85
            $field->table($this->table);
86
            $this->fields[$field->name()] = $field;
87
        }
88
    }
89
90
    /**
91
     * Assigns indexes to model
92
     *
93
     * @param array $indexes
94
     *
95
     * @throws ModelException
96
     */
97
    protected function assignIndexes($indexes)
98
    {
99
        foreach ($indexes as $index) {
100
            if (!$index instanceof IndexInterface) {
101
                throw new ModelException(sprintf('Index must be an instance of IndexInterface, got "%s"', $this->getType($index)));
102
            }
103
104
            foreach ($index->fields() as $key => $field) {
105
                $field = $index->type() == 'foreign' ? $key : $field;
106
107
                $this->assertField($field);
108
            }
109
110
            if ($index->type() !== 'foreign') {
111
                $index->table($this->table);
112
            }
113
114
            $this->indexes[$index->name()] = $index;
115
        }
116
    }
117
118
    /**
119
     * Assigns relations to model
120
     *
121
     * @param array $relations
122
     *
123
     * @throws ModelException
124
     */
125
    protected function assignRelations($relations)
126
    {
127
        foreach ($relations as $relation) {
128
            if (!$relation instanceof RelationInterface) {
129
                throw new ModelException(sprintf('Relation must be an instance of RelationInterface, got "%s"', $this->getType($relation)));
130
            }
131
132
            foreach (array_keys($relation->keys()) as $field) {
133
                $this->assertField($field);
134
            }
135
136
            $this->relations[$relation->name()] = $relation;
137
        }
138
    }
139
140
    /**
141
     * Returns table
142
     *
143
     * @return string
144
     */
145
    public function table()
146
    {
147
        return $this->table;
148
    }
149
150
    /**
151
     * Returns entity class name
152
     *
153
     * @return string
154
     */
155
    public function entity()
156
    {
157
        return $this->entity;
158
    }
159
160
    /**
161
     * Returns alias
162
     *
163
     * @param string $alias
164
     *
165
     * @return string
166
     */
167
    public function alias($alias = null)
168
    {
169
        if ($alias !== null) {
170
            $this->alias = $alias;
171
        }
172
173
        return $this->alias;
174
    }
175
176
    /**
177
     * Returns true if model has field
178
     *
179
     * @param string $field
180
     *
181
     * @return bool
182
     */
183
    public function hasField($field)
184
    {
185
        return isset($this->fields[$field]);
186
    }
187
188
    /**
189
     * Returns array containing field definition
190
     *
191
     * @return FieldInterface[]
192
     */
193
    public function fields()
194
    {
195
        return $this->fields;
196
    }
197
198
    /**
199
     * Asserts if model has field
200
     *
201
     * @param string $field
202
     *
203
     * @throws ModelException
204
     */
205
    protected function assertField($field)
206
    {
207
        if (!$this->hasField($field)) {
208
            throw new ModelException(sprintf('Unknown field, field "%s" not found in model "%s"', $field, $this->entity));
209
        }
210
    }
211
212
    /**
213
     * Returns field definition
214
     *
215
     * @param string $field
216
     *
217
     * @return FieldInterface
218
     * @throws ModelException
219
     */
220
    public function field($field)
221
    {
222
        $this->assertField($field);
223
224
        return $this->fields[$field];
225
    }
226
227
    /**
228
     * Returns array containing names of primary indexes
229
     *
230
     * @return FieldInterface[]
231
     */
232
    public function primaryFields()
233
    {
234
        $result = [];
235
        foreach ($this->indexes as $index) {
236
            if (!$index->isPrimary()) {
237
                continue;
238
            }
239
240
            foreach ($index->fields() as $field) {
241
                $result[] = $this->field($field);
242
            }
243
        }
244
245
        return $result;
246
    }
247
248
    /**
249
     * Returns array of fields from indexes
250
     *
251
     * @return FieldInterface[]
252
     */
253
    public function indexFields()
254
    {
255
        $fields = [];
256
        foreach ($this->indexes as $index) {
257
            $fields = array_merge($fields, $index->fields());
258
        }
259
260
        $result = [];
261
        foreach (array_unique($fields) as $field) {
262
            $result[] = $this->field($field);
263
        }
264
265
        return $result;
266
    }
267
268
    /**
269
     * Returns true if index with set name is defined
270
     *
271
     * @param string $index
272
     *
273
     * @return bool
274
     */
275
    public function hasIndex($index)
276
    {
277
        return isset($this->indexes[$index]);
278
    }
279
280
    /**
281
     * Returns all index definitions
282
     *
283
     * @return IndexInterface[]
284
     */
285
    public function indexes()
286
    {
287
        return $this->indexes;
288
    }
289
290
291
    /**
292
     * Returns index definition
293
     *
294
     * @param string $index
295
     *
296
     * @return IndexInterface[]
297
     * @throws ModelException
298
     */
299
    public function index($index)
300
    {
301
        if (empty($this->indexes[$index])) {
302
            throw new ModelException(sprintf('Unknown index, index "%s" not found in model "%s"', $index, $this->entity));
303
        }
304
305
        return $this->indexes[$index];
306
    }
307
308
    /**
309
     * Returns all relation where field is listed as local key
310
     *
311
     * @param string $field
312
     *
313
     * @return RelationInterface[]
314
     */
315
    public function referredIn($field)
316
    {
317
        $result = [];
318
        foreach ($this->relations as $relation) {
319
            if (false === $i = array_search($field, $relation->localKeys())) {
320
                continue;
321
            }
322
323
            $result[$relation->foreignKeys()[$i]] = $relation;
324
        }
325
326
        return $result;
327
    }
328
329
    /**
330
     * Returns true if at last one relation is defined
331
     *
332
     * @return bool
333
     */
334
    public function hasRelations()
335
    {
336
        return !empty($this->relations);
337
    }
338
339
    /**
340
     * Returns true if relation to passed entity class is defined
341
     *
342
     * @param string $relationName
343
     *
344
     * @return bool
345
     */
346
    public function hasRelation($relationName)
347
    {
348
        return $this->findRelationByName($relationName) !== false;
349
    }
350
351
    /**
352
     * Returns all relation definition
353
     *
354
     * @return RelationInterface[]
355
     */
356
    public function relations()
357
    {
358
        return $this->relations;
359
    }
360
361
    /**
362
     * Returns relation definition for passed entity class
363
     *
364
     * @param string $relationName
365
     *
366
     * @return RelationInterface
367
     * @throws ModelException
368
     */
369
    public function relation($relationName)
370
    {
371
        if (!$relation = $this->findRelationByName($relationName)) {
372
            throw new ModelException(sprintf('Unknown relation, relation "%s" not found in model "%s"', $relationName, $this->entity));
373
        }
374
375
        return $relation;
376
    }
377
378
    /**
379
     * Finds relation by its name
380
     *
381
     * @param string $relationName
382
     *
383
     * @return RelationInterface
384
     */
385
    protected function findRelationByName($relationName)
386
    {
387
        foreach ($this->relations as $relation) {
388
            if ($relation->name() == $relationName || $relation->entity() == $relationName) {
389
                return $relation;
390
            }
391
        }
392
393
        return null;
394
    }
395
}
396