Passed
Push — master ( 268b05...29e480 )
by Anton
01:40
created

Schema::__set_state()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Cycle\ORM;
10
11
use Cycle\ORM\Exception\SchemaException;
12
13
/**
14
 * Static schema with automatic class name => role aliasing.
15
 */
16
final class Schema implements SchemaInterface
17
{
18
    /** @var array */
19
    private $aliases;
20
21
    /** @var array */
22
    private $schema = [];
23
24
    /**
25
     * @param array $schema
26
     */
27
    public function __construct(array $schema)
28
    {
29
        // split into two?
30
        list($this->schema, $this->aliases) = $this->normalize($schema);
31
    }
32
33
    /**
34
     * @inheritdoc
35
     */
36
    public function getRoles(): array
37
    {
38
        return array_keys($this->schema);
39
    }
40
41
    /**
42
     * @inheritdoc
43
     */
44
    public function getRelations(string $role): array
45
    {
46
        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

46
        return array_keys(/** @scrutinizer ignore-type */ $this->define($role, self::RELATIONS));
Loading history...
47
    }
48
49
    /**
50
     * @inheritdoc
51
     */
52
    public function defines(string $role): bool
53
    {
54
        return array_key_exists($role, $this->schema) || array_key_exists($role, $this->aliases);
55
    }
56
57
    /**
58
     * @inheritdoc
59
     */
60
    public function define(string $role, int $property)
61
    {
62
        $role = $this->resolveAlias($role);
63
        if (!isset($this->schema[$role])) {
64
            throw new SchemaException("Undefined schema `{$role}`, not found");
65
        }
66
67
        if (!array_key_exists($property, $this->schema[$role])) {
68
            return null;
69
        }
70
71
        return $this->schema[$role][$property];
72
    }
73
74
    /**
75
     * @inheritdoc
76
     */
77
    public function defineRelation(string $role, string $relation): array
78
    {
79
        $relations = $this->define($role, self::RELATIONS);
80
81
        if (!isset($relations[$relation])) {
82
            throw new SchemaException("Undefined relation `{$role}`.`{$relation}`");
83
        }
84
85
        return $relations[$relation];
86
    }
87
88
    /**
89
     * @inheritdoc
90
     */
91
    public function resolveAlias(string $entity): ?string
92
    {
93
        // walk thought all children until parent entity found
94
        while (isset($this->aliases[$entity])) {
95
            $entity = $this->aliases[$entity];
96
        }
97
98
        return $entity;
99
    }
100
101
102
    /**
103
     * Automatically replace class names with their aliases.
104
     *
105
     * @param array $schema
106
     * @return array Pair of [schema, aliases]
107
     */
108
    protected function normalize(array $schema): array
109
    {
110
        $result = $aliases = [];
111
112
        foreach ($schema as $key => $item) {
113
            $role = $key;
114
            if (!isset($item[self::ENTITY])) {
115
                // legacy type of declaration (class => schema)
116
                $item[self::ENTITY] = $key;
117
            }
118
119
            if (class_exists($key)) {
120
                if (!isset($item[self::ROLE])) {
121
                    throw new SchemaException("Unable to create schema record without given role for `{$key}`");
122
                }
123
124
                $role = $item[self::ROLE];
125
            }
126
127
            if (class_exists($item[self::ENTITY])) {
128
                $aliases[$item[self::ENTITY]] = $key;
129
            }
130
131
            unset($item[self::ROLE]);
132
            $result[$role] = $item;
133
        }
134
135
        // normalizing relation associations
136
        foreach ($result as &$item) {
137
            if (isset($item[self::RELATIONS])) {
138
                $item[self::RELATIONS] = iterator_to_array($this->normalizeRelations(
139
                    $item[self::RELATIONS],
140
                    $aliases
141
                ));
142
            }
143
144
            unset($item);
145
        }
146
147
        return [$result, $aliases];
148
    }
149
150
    /**
151
     * @param array $an_array
152
     * @return Schema
153
     */
154
    public static function __set_state($an_array): Schema
155
    {
156
        $schema = new self([]);
157
        $schema->schema = $an_array['schema'];
158
        $schema->aliases = $an_array['aliases'];
159
160
        return $schema;
161
    }
162
163
    /**
164
     * @param array $relations
165
     * @param array $aliases
166
     * @return \Generator
167
     */
168
    private function normalizeRelations(array $relations, array $aliases): \Generator
169
    {
170
        foreach ($relations as $name => &$rel) {
171
            $target = $rel[Relation::TARGET];
172
            while (isset($aliases[$target])) {
173
                $target = $aliases[$target];
174
            }
175
176
            $rel[Relation::TARGET] = $target;
177
178
            yield $name => $rel;
179
        }
180
    }
181
}