Registry::getRelations()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 0
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema;
6
7
use Cycle\Schema\Definition\Entity;
8
use Cycle\Schema\Exception\RegistryException;
9
use Cycle\Schema\Exception\RelationException;
10
use Cycle\Database\DatabaseProviderInterface;
11
use Cycle\Database\Exception\DBALException;
12
use Cycle\Database\Schema\AbstractTable;
13
14
/**
15
 * @implements \IteratorAggregate<Entity>
16
 */
17
final class Registry implements \IteratorAggregate
18
{
19
    /** @var Entity[] */
20
    private array $entities = [];
21
22
    private DatabaseProviderInterface $dbal;
23
    private \SplObjectStorage $tables;
24
    private \SplObjectStorage $children;
25
    private \SplObjectStorage $relations;
26
    private Defaults $defaults;
27 1366
28
    public function __construct(DatabaseProviderInterface $dbal, ?Defaults $defaults = null)
29 1366
    {
30 1366
        $this->dbal = $dbal;
31 1366
        $this->tables = new \SplObjectStorage();
32 1366
        $this->children = new \SplObjectStorage();
33 1366
        $this->relations = new \SplObjectStorage();
34
        $this->defaults = $defaults ?? new Defaults();
35 1310
    }
36
37 1310
    public function register(Entity $entity): self
38 1194
    {
39 8
        foreach ($this->entities as $e) {
40
            if ($e->getRole() == $entity->getRole()) {
41
                throw new RegistryException("Duplicate entity `{$e->getRole()}`");
42
            }
43 1310
        }
44 1310
45 1310
        $this->entities[] = $entity;
46 1310
        $this->tables[$entity] = null;
47
        $this->children[$entity] = [];
48 1310
        $this->relations[$entity] = [];
49
50
        return $this;
51
    }
52
53
    /**
54 1032
     * @param string $role Entity role of class.
55
     */
56 1032
    public function hasEntity(string $role): bool
57 1032
    {
58 1008
        foreach ($this->entities as $entity) {
59
            if ($entity->getRole() === $role || $entity->getClass() === $role) {
60
                return true;
61
            }
62 40
        }
63
64
        return false;
65
    }
66
67
    /**
68
     * Get entity by it's role.
69
     *
70
     * @param string $role Entity role or class name.
71
     *
72 1192
     * @throws RegistryException
73
     */
74 1192
    public function getEntity(string $role): Entity
75 1184
    {
76 1184
        foreach ($this->entities as $entity) {
77
            if ($entity->getRole() == $role || $entity->getClass() === $role) {
78
                return $entity;
79
            }
80 8
        }
81
82
        throw new RegistryException("Undefined entity `{$role}`");
83
    }
84
85
    public function getIterator(): \Traversable
86 1284
    {
87
        return new \ArrayIterator($this->entities);
88 1284
    }
89
90
    /**
91
     * Assign child entity to parent entity.
92
     * Be careful! This method merges the parent and child entity schemas.
93
     * If you don't need to merge schemas {@see Registry::registerChildWithoutMerge()}.
94
     *
95
     * @throws RegistryException
96 16
     */
97
    public function registerChild(Entity $parent, Entity $child): void
98 16
    {
99 8
        $this->registerChildWithoutMerge($parent, $child);
100
101
        // merge parent and child schema
102 8
        $parent->merge($child);
103 8
    }
104 8
105
    public function registerChildWithoutMerge(Entity $parent, Entity $child): void
106
    {
107 8
        if (!$this->hasInstance($parent)) {
108 8
            throw new RegistryException("Undefined entity `{$parent->getRole()}`");
109
        }
110
111
        $children = $this->children[$parent];
112
        $children[] = $child;
113
        $this->children[$parent] = $children;
114
    }
115
116
    /**
117
     * Get all assigned children entities.
118
     *
119
     * @return Entity[]
120
     */
121
    public function getChildren(Entity $entity): array
122
    {
123
        if (!$this->hasInstance($entity)) {
124
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
125
        }
126
127
        return $this->children[$entity];
128
    }
129
130 1264
    /**
131
     * Associate entity with table.
132 1264
     *
133 8
     * @param non-empty-string $table
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
134
     *
135
     * @throws RegistryException
136 1256
     * @throws DBALException
137
     */
138 1256
    public function linkTable(Entity $entity, ?string $database, string $table): self
139 1256
    {
140 1256
        if (!$this->hasInstance($entity)) {
141
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
142 1256
        }
143 1256
144
        $database = $this->dbal->database($database)->getName();
145
146
        $schema = null;
147 1176
        foreach ($this->tables as $other) {
148 56
            $association = $this->tables[$other];
149 56
150
            if ($association === null) {
151
                continue;
152
            }
153 1256
154 1256
            // avoid schema duplication
155
            if ($association['database'] === $database && $association['table'] === $table) {
156
                $schema = $association['schema'];
157 1256
                break;
158 1256
            }
159 1256
        }
160 1256
161
        if ($schema === null) {
0 ignored issues
show
introduced by
The condition $schema === null is always true.
Loading history...
162
            $dbTable = $this->dbal->database($database)->table($table);
163 1256
            if (!\method_exists($dbTable, 'getSchema')) {
164
                throw new RegistryException('Unable to retrieve table schema.');
165
            }
166
            $schema = $dbTable->getSchema();
167
        }
168
169 960
        $this->tables[$entity] = [
170
            'database' => $database,
171 960
            'table' => $table,
172 32
            'schema' => $schema,
173
        ];
174
175 928
        return $this;
176
    }
177
178
    /**
179
     * @throws RegistryException
180
     */
181 896
    public function hasTable(Entity $entity): bool
182
    {
183 896
        if (!$this->hasInstance($entity)) {
184
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
185
        }
186
187 888
        return $this->tables[$entity] !== null;
188
    }
189
190
    /**
191
     * @throws RegistryException
192
     */
193 832
    public function getDatabase(Entity $entity): string
194
    {
195 832
        if (!$this->hasTable($entity)) {
196
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
197
        }
198
199 824
        return $this->tables[$entity]['database'];
200
    }
201
202
    /**
203
     * @return non-empty-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
204
     * @throws RegistryException
205 416
     *
206
     */
207 416
    public function getTable(Entity $entity): string
208
    {
209
        if (!$this->hasTable($entity)) {
210
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
211 408
        }
212
213
        return $this->tables[$entity]['table'];
214
    }
215
216
    /**
217
     * @throws RegistryException
218
     */
219
    public function getTableSchema(Entity $entity): AbstractTable
220 984
    {
221
        if (!$this->hasTable($entity)) {
222 984
            throw new RegistryException("Entity `{$entity->getRole()}` has no assigned table");
223
        }
224
225
        return $this->tables[$entity]['schema'];
226 984
    }
227 984
228 984
    /**
229 984
     * Create entity relation.
230
     *
231
     * @throws RegistryException
232
     * @throws RelationException
233
     */
234 392
    public function registerRelation(Entity $entity, string $name, RelationInterface $relation): void
235
    {
236 392
        if (!$this->hasInstance($entity)) {
237
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
238
        }
239
240 392
        $relations = $this->relations[$entity];
241
        $relations[$name] = $relation;
242
        $this->relations[$entity] = $relations;
243 392
    }
244
245 392
    /**
246
     * @throws RegistryException
247
     */
248
    public function hasRelation(Entity $entity, string $name): bool
249 392
    {
250
        if (!$this->hasInstance($entity)) {
251
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
252
        }
253
254
        return isset($this->relations[$entity][$name]);
255
    }
256
257 816
    public function getRelation(Entity $entity, string $name): RelationInterface
258
    {
259 816
        if (!$this->hasRelation($entity, $name)) {
260
            throw new RegistryException("Undefined relation `{$entity->getRole()}`.`{$name}`");
261
        }
262
263 816
        return $this->relations[$entity][$name];
264
    }
265
266 1320
    /**
267
     * Get all relations assigned with given entity.
268 1320
     *
269
     * @return RelationInterface[]
270
     */
271
    public function getRelations(Entity $entity): array
272
    {
273
        if (!$this->hasInstance($entity)) {
274
            throw new RegistryException("Undefined entity `{$entity->getRole()}`");
275
        }
276
277
        return $this->relations[$entity];
278
    }
279
280
    public function getDefaults(): Defaults
281
    {
282
        return $this->defaults;
283
    }
284
285
    protected function hasInstance(Entity $entity): bool
286
    {
287
        return array_search($entity, $this->entities, true) !== false;
288
    }
289
}
290