Passed
Push — master ( 280395...84f285 )
by Anton
01:48
created

Schema::collectClasses()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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