ModelSchemaInfo   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 463
Duplicated Lines 12.31 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 0
dl 57
loc 463
ccs 146
cts 146
cp 1
rs 9.68
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A getTable() 0 8 1
A getPrimaryKey() 0 8 1
A hasAttributeType() 8 8 1
A getAttributeLengths() 0 8 1
A getAttributes() 0 8 1
A getRawAttributes() 0 8 1
A getData() 0 18 1
A setData() 0 9 1
A registerClass() 0 34 4
A hasClass() 0 12 2
A getAttributeTypes() 0 8 1
A getAttributeType() 0 11 1
A hasAttributeLength() 8 8 1
A getAttributeLength() 11 11 1
A hasRelationship() 0 6 1
A getRelationshipType() 11 11 1
A getReverseRelationship() 0 6 1
A getReversePrimaryKey() 9 9 1
A getReverseForeignKey() 10 10 1
A getReverseModelClass() 0 6 1
A getForeignKey() 0 6 1
A getBelongsToManyRelationship() 0 6 1
A registerBelongsToOneRelationship() 0 17 1
A registerBelongsToManyRelationship() 0 23 1
A registerRelationshipType() 0 10 2
A registerReversedRelationship() 0 20 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Application\Data;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use InvalidArgumentException;
22
use Limoncello\Contracts\Data\ModelSchemaInfoInterface;
23
use Limoncello\Contracts\Data\RelationshipTypes;
24
use ReflectionClass;
25
use ReflectionException;
26
use function array_change_key_case;
27
use function array_key_exists;
28
use function array_keys;
29
use function assert;
30
use function strtolower;
31
32
/**
33
 * @package Limoncello\Application
34
 *
35
 * @SuppressWarnings(PHPMD.LongVariable)
36
 */
37
class ModelSchemaInfo implements ModelSchemaInfoInterface
38
{
39
    /**
40
     * @var array
41
     */
42
    private $relationshipTypes = [];
43
44
    /**
45
     * @var array
46
     */
47
    private $reversedRelationships = [];
48
49
    /**
50
     * @var array
51
     */
52
    private $reversedClasses = [];
53
54
    /**
55
     * @var array
56
     */
57
    private $foreignKeys = [];
58
59
    /**
60
     * @var array
61
     */
62
    private $belongsToMany = [];
63
64
    /**
65
     * @var array
66
     */
67
    private $tableNames = [];
68
69
    /**
70
     * @var array
71
     */
72
    private $primaryKeys = [];
73
74
    /**
75
     * @var array
76
     */
77
    private $attributeTypes = [];
78
79
    /**
80
     * @var array
81
     */
82
    private $attributeLengths = [];
83
84
    /**
85
     * @var array
86
     */
87
    private $attributes = [];
88 8
89
    /**
90
     * @var array
91 8
     */
92 8
    private $rawAttributes = [];
93 8
94 8
    /**
95 8
     * @return array
96 8
     */
97 8
    public function getData(): array
98 8
    {
99 8
        $result = [
100 8
            $this->foreignKeys,
101 8
            $this->belongsToMany,
102
            $this->relationshipTypes,
103
            $this->reversedRelationships,
104 8
            $this->tableNames,
105
            $this->primaryKeys,
106
            $this->attributeTypes,
107
            $this->attributeLengths,
108
            $this->attributes,
109
            $this->rawAttributes,
110
            $this->reversedClasses,
111
        ];
112 7
113
        return $result;
114 7
    }
115 7
116 7
    /**
117 7
     * @param array $data
118
     *
119 7
     * @return self
120
     */
121
    public function setData(array $data): self
122
    {
123
        list($this->foreignKeys, $this->belongsToMany, $this->relationshipTypes,
124
            $this->reversedRelationships,$this->tableNames, $this->primaryKeys,
125
            $this->attributeTypes, $this->attributeLengths, $this->attributes, $this->rawAttributes,
126
            $this->reversedClasses) = $data;
127 8
128
        return $this;
129
    }
130
131
    /** @noinspection PhpTooManyParametersInspection
132
     * @inheritdoc
133
     *
134
     * @throws ReflectionException
135 8
     */
136 1
    public function registerClass(
137
        string $class,
138
        string $tableName,
139 8
        string $primaryKey,
140 1
        array $attributeTypes,
141
        array $attributeLengths,
142
        array $rawAttributes = []
143 8
    ): ModelSchemaInfoInterface {
144 1
        if (empty($class) === true) {
145
            throw new InvalidArgumentException('class');
146
        }
147 8
148 8
        if (empty($tableName) === true) {
149 8
            throw new InvalidArgumentException('tableName');
150
        }
151
152 8
        if (empty($primaryKey) === true) {
153 8
            throw new InvalidArgumentException('primaryKey');
154 8
        }
155 8
156 8
        assert(
157 8
            (new ReflectionClass($class))->getName() === $class,
158
            "Please check name for class `$class`. It should be case sensitive."
159 8
        );
160
161
        $this->tableNames[$class]       = $tableName;
162
        $this->primaryKeys[$class]      = $primaryKey;
163
        $this->attributeTypes[$class]   = $attributeTypes;
164
        $this->attributeLengths[$class] = $attributeLengths;
165 2
        $this->attributes[$class]       = array_keys($attributeTypes);
166
        $this->rawAttributes[$class]    = $rawAttributes;
167 2
168
        return $this;
169
    }
170 2
171 2
    /**
172 2
     * @inheritdoc
173
     */
174
    public function hasClass(string $class): bool
175 2
    {
176
        $result = array_key_exists($class, $this->tableNames);
177
178
        // check if not found it cannot be found case insensitive (protection from case insensitive values)
179
        assert(
180
            $result === true ||
181 2
            in_array(strtolower($class), array_change_key_case($this->tableNames, CASE_LOWER)) === false
182
        );
183 2
184
        return $result;
185 2
    }
186
187 2
    /**
188
     * @inheritdoc
189
     */
190
    public function getTable(string $class): string
191
    {
192
        assert($this->hasClass($class));
193 2
194
        $result = $this->tableNames[$class];
195 2
196
        return $result;
197 2
    }
198
199 2
    /**
200
     * @inheritdoc
201
     */
202
    public function getPrimaryKey(string $class): string
203
    {
204
        assert($this->hasClass($class));
205 1
206
        $result = $this->primaryKeys[$class];
207 1
208
        return $result;
209 1
    }
210
211 1
    /**
212
     * @inheritdoc
213
     */
214
    public function getAttributeTypes(string $class): array
215
    {
216
        assert($this->hasClass($class));
217 1
218
        $result = $this->attributeTypes[$class];
219 1
220 1
        return $result;
221 1
    }
222
223
    /**
224 1
     * @inheritdoc
225
     */
226 1
    public function getAttributeType(string $class, string $name): string
227
    {
228
        assert(
229
            $this->hasAttributeType($class, $name),
230
            "Type is not defined for attribute `$name` in class `$class`."
231
        );
232 1
233
        $result = $this->attributeTypes[$class][$name];
234 1
235
        return $result;
236 1
    }
237
238 1
    /**
239
     * @inheritdoc
240
     */
241 View Code Duplication
    public function hasAttributeType(string $class, string $name): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
242
    {
243
        assert($this->hasClass($class));
244 1
245
        $result = isset($this->attributeTypes[$class][$name]);
246 1
247
        return $result;
248 1
    }
249
250 1
    /**
251
     * @inheritdoc
252
     */
253
    public function getAttributeLengths(string $class): array
254
    {
255
        assert($this->hasClass($class));
256 1
257
        $result = $this->attributeLengths[$class];
258 1
259
        return $result;
260 1
    }
261
262 1
    /**
263
     * @inheritdoc
264
     */
265 View Code Duplication
    public function hasAttributeLength(string $class, string $name): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266
    {
267
        assert($this->hasClass($class));
268 1
269
        $result = isset($this->attributeLengths[$class][$name]);
270 1
271 1
        return $result;
272 1
    }
273
274
    /**
275 1
     * @inheritdoc
276
     */
277 1 View Code Duplication
    public function getAttributeLength(string $class, string $name): int
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
    {
279
        assert(
280
            $this->hasAttributeLength($class, $name) === true,
281
            "Length not found for column `$name` in class `$class`."
282
        );
283 1
284
        $result = $this->attributeLengths[$class][$name];
285 1
286
        return $result;
287 1
    }
288
289 1
    /**
290
     * @inheritdoc
291
     */
292
    public function getAttributes(string $class): array
293
    {
294
        assert($this->hasClass($class));
295 1
296
        $result = $this->attributes[$class];
297 1
298
        return $result;
299 1
    }
300
301 1
    /**
302
     * @inheritdoc
303
     */
304
    public function getRawAttributes(string $class): array
305
    {
306
        assert($this->hasClass($class));
307 2
308
        $result = $this->rawAttributes[$class];
309 2
310
        return $result;
311 2
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316
    public function hasRelationship(string $class, string $name): bool
317 2
    {
318
        $result = isset($this->relationshipTypes[$class][$name]);
319 2
320 2
        return $result;
321 2
    }
322
323
    /**
324 2
     * @inheritdoc
325
     */
326 2 View Code Duplication
    public function getRelationshipType(string $class, string $name): int
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
    {
328
        assert(
329
            $this->hasRelationship($class, $name) === true,
330
            "Relationship `$name` not found in class `$class`."
331
        );
332 2
333
        $result = $this->relationshipTypes[$class][$name];
334 2
335
        return $result;
336 2
    }
337
338
    /**
339
     * @inheritdoc
340
     */
341
    public function getReverseRelationship(string $class, string $name): array
342 1
    {
343
        $result = $this->reversedRelationships[$class][$name];
344 1
345
        return $result;
346 1
    }
347 1
348
    /**
349 1
     * @inheritdoc
350
     */
351 View Code Duplication
    public function getReversePrimaryKey(string $class, string $name): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
352
    {
353
        $reverseClass = $this->getReverseModelClass($class, $name);
354
355 1
        $table = $this->getTable($reverseClass);
356
        $key   = $this->getPrimaryKey($reverseClass);
357 1
358
        return [$key, $table];
359 1
    }
360
361 1
    /**
362
     * @inheritdoc
363 1
     */
364 View Code Duplication
    public function getReverseForeignKey(string $class, string $name): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
365
    {
366
        list ($reverseClass, $reverseName) = $this->getReverseRelationship($class, $name);
367
368
        $table = $this->getTable($reverseClass);
369 1
        // would work only if $name is hasMany relationship
370
        $key   = $this->getForeignKey($reverseClass, $reverseName);
371 1
372
        return [$key, $table];
373 1
    }
374
375
    /**
376
     * @inheritdoc
377
     */
378
    public function getReverseModelClass(string $class, string $name): string
379 1
    {
380
        $reverseClass = $this->reversedClasses[$class][$name];
381 1
382
        return $reverseClass;
383 1
    }
384
385
    /**
386
     * @inheritdoc
387
     */
388
    public function getForeignKey(string $class, string $name): string
389 1
    {
390
        $result = $this->foreignKeys[$class][$name];
391 1
392
        return $result;
393 1
    }
394
395
    /**
396
     * @inheritdoc
397
     */
398
    public function getBelongsToManyRelationship(string $class, string $name): array
399 8
    {
400
        $result = $this->belongsToMany[$class][$name];
401
402
        return $result;
403
    }
404
405
    /**
406 8
     * @inheritdoc
407 8
     */
408
    public function registerBelongsToOneRelationship(
409 8
        string $class,
410 8
        string $name,
411
        string $foreignKey,
412 8
        string $reverseClass,
413
        string $reverseName
414 8
    ): ModelSchemaInfoInterface {
415
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO, $class, $name);
416
        $this->registerRelationshipType(RelationshipTypes::HAS_MANY, $reverseClass, $reverseName);
417
418
        $this->registerReversedRelationship($class, $name, $reverseClass, $reverseName);
419
        $this->registerReversedRelationship($reverseClass, $reverseName, $class, $name);
420 8
421
        $this->foreignKeys[$class][$name] = $foreignKey;
422
423
        return $this;
424
    }
425
426
    /** @noinspection PhpTooManyParametersInspection
427
     * @inheritdoc
428
     */
429 8
    public function registerBelongsToManyRelationship(
430 8
        string $class,
431
        string $name,
432
        string $table,
433
        string $foreignKey,
434
        string $reverseForeignKey,
435 8
        string $reverseClass,
436 8
        string $reverseName
437
    ): ModelSchemaInfoInterface {
438 8
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO_MANY, $class, $name);
439 8
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO_MANY, $reverseClass, $reverseName);
440
441 8
        // NOTE:
442
        // `registerReversedRelationship` relies on duplicate registration check in `registerRelationshipType`
443
        // so it must be called afterwards
444
        $this->registerReversedRelationship($class, $name, $reverseClass, $reverseName);
445
        $this->registerReversedRelationship($reverseClass, $reverseName, $class, $name);
446
447
        $this->belongsToMany[$class][$name]               = [$table, $foreignKey, $reverseForeignKey];
448
        $this->belongsToMany[$reverseClass][$reverseName] = [$table, $reverseForeignKey, $foreignKey];
449
450
        return $this;
451 8
    }
452
453 8
    /**
454 8
     * @param int    $type
455 8
     * @param string $class
456 8
     * @param string $name
457
     *
458
     * @return void
459 8
     */
460
    private function registerRelationshipType(int $type, string $class, string $name): void
461
    {
462
        assert(empty($class) === false && empty($name) === false);
463
        assert(
464
            isset($this->relationshipTypes[$class][$name]) === false,
465
            "Relationship `$name` for class `$class` was already used."
466
        );
467
468
        $this->relationshipTypes[$class][$name] = $type;
469
    }
470 8
471
    /**
472
     * @param string $class
473
     * @param string $name
474
     * @param string $reverseClass
475
     * @param string $reverseName
476 8
     *
477 8
     * @return void
478 8
     */
479 8
    private function registerReversedRelationship(
480 8
        string $class,
481
        string $name,
482
        string $reverseClass,
483
        string $reverseName
484
    ): void {
485
        assert(
486
            empty($class) === false &&
487 8
            empty($name) === false &&
488 8
            empty($reverseClass) === false &&
489
            empty($reverseName) === false
490
        );
491
492
        // NOTE:
493
        // this function relies it would be called after
494
        // `registerRelationshipType` which prevents duplicate registrations
495
496
        $this->reversedRelationships[$class][$name] = [$reverseClass, $reverseName];
497
        $this->reversedClasses[$class][$name]       = $reverseClass;
498
    }
499
}
500