Passed
Push — master ( 84f285...298836 )
by Anton
01:51
created

Schema   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 148
rs 10
c 0
b 0
f 0
wmc 22

9 Methods

Rating   Name   Duplication   Size   Complexity  
A define() 0 12 3
A __construct() 0 4 1
A defines() 0 3 2
A getRoles() 0 3 1
A defineRelation() 0 9 2
B normalize() 0 38 7
A getRelations() 0 3 1
A normalizeRelations() 0 11 3
A resolveAlias() 0 8 2
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
namespace Spiral\Cycle;
11
12
use Spiral\Cycle\Exception\SchemaException;
13
14
/**
15
 * Static schema with automatic class name => role aliasing.
16
 */
17
final class Schema implements SchemaInterface
18
{
19
    /** @var array */
20
    private $aliases;
21
22
    /** @var array */
23
    private $schema = [];
24
25
    /**
26
     * @param array $schema
27
     */
28
    public function __construct(array $schema)
29
    {
30
        // split into two?
31
        list($this->schema, $this->aliases) = $this->normalize($schema);
32
    }
33
34
    /**
35
     * @inheritdoc
36
     */
37
    public function getRoles(): array
38
    {
39
        return array_keys($this->schema);
40
    }
41
42
    /**
43
     * @inheritdoc
44
     */
45
    public function defines(string $role): bool
46
    {
47
        return array_key_exists($role, $this->schema) || array_key_exists($role, $this->aliases);
48
    }
49
50
    /**
51
     * @inheritdoc
52
     */
53
    public function define(string $role, int $property)
54
    {
55
        $role = $this->resolveAlias($role);
56
        if (!isset($this->schema[$role])) {
57
            throw new SchemaException("Undefined schema `{$role}`, not found");
58
        }
59
60
        if (!array_key_exists($property, $this->schema[$role])) {
61
            return null;
62
        }
63
64
        return $this->schema[$role][$property];
65
    }
66
67
    /**
68
     * @inheritdoc
69
     */
70
    public function getRelations(string $role): array
71
    {
72
        return array_keys($this->define($role, self::RELATIONS));
0 ignored issues
show
Bug introduced by
It seems like $this->define($role, self::RELATIONS) can also be of type null; however, parameter $input of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

72
        return array_keys(/** @scrutinizer ignore-type */ $this->define($role, self::RELATIONS));
Loading history...
73
    }
74
75
    /**
76
     * @inheritdoc
77
     */
78
    public function defineRelation(string $role, string $relation): array
79
    {
80
        $relations = $this->define($role, self::RELATIONS);
81
82
        if (!isset($relations[$relation])) {
83
            throw new SchemaException("Undefined relation `{$role}`.`{$relation}`");
84
        }
85
86
        return $relations[$relation];
87
    }
88
89
    /**
90
     * @inheritdoc
91
     */
92
    public function resolveAlias(string $entity): ?string
93
    {
94
        // walk thought all children until parent entity found
95
        while (isset($this->aliases[$entity])) {
96
            $entity = $this->aliases[$entity];
97
        }
98
99
        return $entity;
100
    }
101
102
103
    /**
104
     * Automatically replace class names with their aliases.
105
     *
106
     * @param array $schema
107
     * @return array Pair of [schema, aliases]
108
     */
109
    protected function normalize(array $schema): array
110
    {
111
        $result = $aliases = [];
112
113
        foreach ($schema as $key => $item) {
114
            $role = $key;
115
            if (!isset($item[self::ENTITY])) {
116
                // legacy type of declaration (class => schema)
117
                $item[self::ENTITY] = $key;
118
            }
119
120
            if (class_exists($key)) {
121
                if (!isset($item[self::ROLE])) {
122
                    throw new SchemaException("Unable to create schema record without given role for `{$key}`");
123
                }
124
125
                // class => role type of definition
126
                $aliases[$key] = $item[self::ROLE];
127
                $role = $item[self::ROLE];
128
            }
129
130
            unset($item[self::ROLE]);
131
            $result[$role] = $item;
132
        }
133
134
        // normalizing relation associations
135
        foreach ($result as &$item) {
136
            if (isset($item[self::RELATIONS])) {
137
                $item[self::RELATIONS] = iterator_to_array($this->normalizeRelations(
138
                    $item[self::RELATIONS],
139
                    $aliases
140
                ));
141
            }
142
143
            unset($item);
144
        }
145
146
        return [$result, $aliases];
147
    }
148
149
    /**
150
     * @param array $relations
151
     * @param array $aliases
152
     * @return \Generator
153
     */
154
    private function normalizeRelations(array $relations, array $aliases): \Generator
155
    {
156
        foreach ($relations as $name => &$rel) {
157
            $target = $rel[Relation::TARGET];
158
            while (isset($aliases[$target])) {
159
                $target = $aliases[$target];
160
            }
161
162
            $rel[Relation::TARGET] = $target;
163
164
            yield $name => $rel;
165
        }
166
    }
167
}